Compare commits

...

7 Commits

Author SHA1 Message Date
5a6f678fbf
merge CAN fixup from master-test 2025-05-17 22:23:39 +02:00
4be1355c40
add minimum duration of 1 sec for precharge 2025-05-16 23:10:07 +02:00
aa85867083
cleanup part 5 2025-05-09 19:09:41 +02:00
1edc8d3e1c
cleanup part 4 2025-05-09 14:49:16 +02:00
d4dc7e4533
cleanup part 3 2025-05-09 02:08:11 +02:00
686e26b609
cleanup part 2 2025-05-09 00:34:08 +02:00
2c0d7350e3
cleanup part 1 2025-05-07 23:44:38 +02:00
41 changed files with 1566 additions and 11857 deletions

View File

@ -0,0 +1,2 @@
IndentWidth: 4
ColumnLimit: 120

File diff suppressed because one or more lines are too long

View File

@ -90,8 +90,8 @@
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/build/debug/AMS_Master_Nucleo.elf",
"cwd": "c:/Users/lenex/Desktop/ams-master/AMS_Master_Code/Core/Lib/ADBMS6830B_Driver/Core/Src",
"program": "c:/Users/lenex/Desktop/ams-master/AMS_Master_Code/Core/Lib/ADBMS6830B_Driver/Core/Src/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [

View File

@ -1,3 +1,5 @@
#ifndef INC_NTC_H_
#define INC_NTC_H_
#include <math.h>
#include <stdint.h>
@ -21,9 +23,10 @@
[[gnu::optimize("fast-math")]]
static inline uint16_t ntc_mv_to_celsius(int16_t adc) {
float log_ohms = logf(1 / ((VREF / adc) - 1));
const float log_ohms = logf(1 / ((VREF / adc) - 1));
return (uint16_t) (TEMP_CONV / (NTC_A1 + NTC_B1 * log_ohms + NTC_C1 * log_ohms * log_ohms + NTC_D1 * log_ohms * log_ohms * log_ohms) - CELSIUS_TO_KELVIN_SCALED);
}
#else
// Lookup Table coming soon; not really needed but fun?
#endif
#endif

View File

@ -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();

View File

@ -1,23 +1,13 @@
#pragma once
#ifndef __CONFIG_ADBMS6830_H
#define __CONFIG_ADBMS6830_H
#include "main.h"
#define N_BMS 1
#define N_CELLS 16
#define ADBMS_MAX_CHIP_TEMP 110 // max temperature of ADBMS6830B (not battery) in C
#define ADBMS_MAX_CHIP_TEMP 1100 // max temperature of ADBMS6830B (not battery) in C
#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

View File

@ -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

View File

@ -11,6 +11,8 @@
// Time to wait after reaching 95% of battery voltage before exiting precharge
// Set this to 1000 in scruti to demonstrate the voltage on the multimeter
#define PRECHARGE_95_DURATION 0 // ms
// Minimum precharge time
#define PRECHARGE_MIN_DURATION 1000 // ms
// Time to wait for discharge
#define DISCHARGE_DURATION 5000 // ms
// Time to wait after there is no more error condition before exiting TS_ERROR

View File

@ -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
---

View File

@ -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

View File

@ -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_ */

View File

@ -1,18 +0,0 @@
//
// Created by kbracher on 2/6/25.
//
#ifndef AMS_MASTER_CODE_ADBMS_ERROR_H
#define AMS_MASTER_CODE_ADBMS_ERROR_H
#include "ADBMS_Driver.h"
#include <limits.h>
static_assert(sizeof(error_sources) * CHAR_BIT >= NUM_ERROR_KINDS,
"error_sources is too small to hold all error sources");
void set_error_source(ADBMS_Status source);
void clear_error_source(ADBMS_Status source);
#endif //AMS_MASTER_CODE_ADBMS_ERROR_H

View File

@ -1,33 +0,0 @@
/*
* AMS_HighLevel.h
*
* Created on: 20.07.2022
* Author: max
*/
#ifndef INC_AMS_HIGHLEVEL_H_
#define INC_AMS_HIGHLEVEL_H_
#include "ADBMS_Abstraction.h"
#include "ADBMS_CMD_MAKROS.h"
#include "ADBMS_LL_Driver.h"
#include "config_ADBMS6830.h"
#include <stdbool.h>
typedef enum {
AMSDEACTIVE,
AMSIDLE,
AMSCHARGING,
AMSIDLEBALANCING,
AMSDISCHARGING,
AMSWARNING,
AMSERROR
} amsState;
extern amsState currentAMSState;
extern Cell_Module modules[N_BMS];
extern uint32_t balancedCells;
extern bool BalancingActive;
#endif /* INC_AMS_HIGHLEVEL_H_ */

View File

@ -1,17 +0,0 @@
#include "ADBMS_Error.h"
#include "stm32h7xx_hal.h"
SlaveErrorData error_data[NUM_ERROR_KINDS] = {};
uint32_t error_sources = 0;
void set_error_source(ADBMS_Status source) {
if (!(error_sources & (1 << source))) {
error_data[source].errors_since = HAL_GetTick();
}
error_sources |= (1 << source);
}
void clear_error_source(ADBMS_Status source) {
error_data[source].errors_since = 0;
error_sources &= ~(1 << source);
}

View File

@ -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_ */

View File

@ -1,14 +1,6 @@
/*
* ADBMS_CMD_MAKROS.h
*
* Created on: 14.07.2022
* Author: max
*/
#ifndef INC_ADBMS_CMD_MAKROS_H_
#define INC_ADBMS_CMD_MAKROS_H_
#include <stdint.h>
#define WRCFGA 0x0001 // Write Configuration Register Group A
#define RDCFGA 0x0002 // Read Configuration Register Group A
#define WRCFGB 0x0024 // Write Configuration Register Group B

View File

@ -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,13 +86,11 @@ typedef struct {
int16_t cellVoltages[MAXIMUM_CELL_VOLTAGES];
int16_t auxVoltages[MAXIMUM_AUX_VOLTAGES];
} Cell_Module;
} BMS_Chip;
extern uint32_t error_sources; // Bitfield of error sources
extern SlaveErrorData error_data[NUM_ERROR_KINDS];
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();

View File

@ -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

View File

@ -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_ */

View File

@ -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,31 +71,29 @@ 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++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_C_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_C_SIZE);
(*module)[i].status.CS_FLT = rxbuf[offset + 0] | (rxbuf[offset + 1] << 8);
(*module)[i].status.CCTS = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8);
(*module)[i].status.VA_OV = (rxbuf[offset + 4] >> 7) & 0x01;
@ -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};
@ -136,7 +126,7 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
for (size_t group = 0; group < 4; group++) {
CHECK_RETURN(readCMD(auxCommands[group], rxbuf, auxGroupSizes[group]));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, auxGroupSizes[group]);
const size_t offset = BUFFER_BMS_OFFSET(i, auxGroupSizes[group]);
for (size_t j = 0; j < auxVoltagesPerGroup[group]; j++) {
(*module)[i].auxVoltages[group * 3 + j] = mV_from_ADBMS6830(rxbuf[offset + j * 2] | (rxbuf[offset + j * 2 + 1] << 8));
}
@ -146,13 +136,13 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDSTATA, rxbuf, STATUS_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_A_SIZE);
(*module)[i].internalDieTemp = (mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8)) / 7.5f) - 273; //datasheet page 72
}
CHECK_RETURN(readCMD(RDSTATB, rxbuf, STATUS_GROUP_B_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_B_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_B_SIZE);
(*module)[i].digitalSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
(*module)[i].analogSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8));
(*module)[i].refVoltage = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
@ -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)] = {};
@ -175,12 +165,12 @@ HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint
CHECK_RETURN(readCMD(RDPWMB, rxbufB, PWM_GROUP_B_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE);
size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE);
const size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE);
const size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE);
for (size_t c = 0; c < 16; c += 2) {
uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0;
uint8_t low = (channels[i] & (1 << c)) ? dutyCycle : 0;
const uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0;
const uint8_t low = (channels[i] & (1 << c)) ? dutyCycle : 0;
if (c < 12) {
rxbufA[offsetA + (c / 2)] = high | low;
@ -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,12 +216,12 @@ 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++) {
size_t offset = BUFFER_BMS_OFFSET(i, CFG_GROUP_B_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CFG_GROUP_B_SIZE);
// UV
buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF);
@ -247,13 +237,13 @@ 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));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE);
uint32_t ov_uv_data = 0;
ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) | (regbuffer[offset + 2] << 16) |
(regbuffer[offset + 3] << 24);
@ -272,24 +262,24 @@ 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));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*module)[i].cellVoltages[0] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
(*module)[i].cellVoltages[1] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
(*module)[i].cellVoltages[2] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
@ -297,7 +287,7 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVB, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*module)[i].cellVoltages[3] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
(*module)[i].cellVoltages[4] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
(*module)[i].cellVoltages[5] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
@ -305,7 +295,7 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVC, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*module)[i].cellVoltages[6] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
(*module)[i].cellVoltages[7] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
(*module)[i].cellVoltages[8] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
@ -313,7 +303,7 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVD, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*module)[i].cellVoltages[9] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
(*module)[i].cellVoltages[10] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
(*module)[i].cellVoltages[11] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
@ -321,7 +311,7 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVE, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*module)[i].cellVoltages[12] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
(*module)[i].cellVoltages[13] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
(*module)[i].cellVoltages[14] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
@ -329,16 +319,16 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVF, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
(*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)] = {};
@ -352,7 +342,7 @@ HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
}
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << i))) {
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
@ -362,12 +352,12 @@ HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
}
}
size_t packet_count =
const size_t packet_count =
((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0); // number of 3 byte packets needed to send all data
for (size_t i = 0; i < (packet_count * 3); i++) { //i - 1 is the number of data bytes sent so far (1 byte for the address)
for (size_t j = 0; j < N_BMS; j++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected
@ -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)] = {};
@ -426,7 +416,7 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
}
for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << i))) {
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
@ -436,7 +426,7 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
}
}
size_t packet_count = ((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0);
const size_t packet_count = ((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0);
for (size_t i = 0; i < (packet_count * 3);
i++) { // i - 1 is the number of data bytes sent so far (1 byte for the address)
@ -480,7 +470,7 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
for (size_t j = 0; j < N_BMS; j++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected
@ -504,7 +494,7 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
for (size_t j = 0; j < N_BMS; j++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
const size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected
@ -518,5 +508,5 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
}
}
return HAL_OK;
return ADBMS_OK;
}

View File

@ -1,24 +1,12 @@
/*
* AMS_HighLevel.c
*
* Created on: 20.07.2022
* Author: max
*/
#include "ADBMS_HighLevel.h"
#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] = {};
uint32_t balancedCells = 0;
bool balancingActive = false;
BMS_Chip bms_data[N_BMS] = {};
uint8_t packetChecksumFails = 0;
#define MAX_PACKET_CHECKSUM_FAILS 5
@ -35,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;
@ -53,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; \
@ -68,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))) {
@ -76,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();
}
}
@ -87,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};

View File

@ -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,31 +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(uint8_t* buffer, uint8_t buffersize) {
HAL_StatusTypeDef status;
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, 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
@ -70,17 +46,17 @@ static uint16_t computeCRC15(const uint8_t* data, size_t length) {
static uint8_t calculateCommandPEC(uint8_t* data, uint8_t datalen) {
if (datalen < 3) { return 1; }
uint16_t pec = computeCRC15(data, datalen - 2);
const uint16_t pec = computeCRC15(data, datalen - 2);
data[datalen - 2] = (uint8_t)((pec >> 7) & 0xFF);
data[datalen - 1] = (uint8_t)((pec << 1) & 0xFF);
return 0;
}
static uint8_t checkCommandPEC(uint8_t* data, uint8_t datalen) {
static uint8_t checkCommandPEC(const uint8_t* data, uint8_t datalen) {
if (datalen <= 3) { return 255; }
uint16_t pec = computeCRC15(data, datalen - 2);
uint8_t pech = (uint8_t)((pec >> 7) & 0xFF);
uint8_t pecl = (uint8_t)((pec << 1) & 0xFF);
const uint16_t pec = computeCRC15(data, datalen - 2);
const uint8_t pech = (uint8_t)((pec >> 7) & 0xFF);
const uint8_t pecl = (uint8_t)((pec << 1) & 0xFF);
return ((pech == data[datalen - 2]) && (pecl == data[datalen - 1])) ? 0 : 1;
}
@ -118,22 +94,24 @@ static uint16_t computeCRC10(const uint8_t* data, size_t length, bool rx_cmd) {
static uint8_t calculateDataPEC(uint8_t* data, uint8_t datalen) {
if (datalen < 3) { return 1; }
uint16_t crcVal = computeCRC10(data, datalen - 2, true);
const uint16_t crcVal = computeCRC10(data, datalen - 2, true);
data[datalen - 2] = (uint8_t)((crcVal >> 8) & 0xFF);
data[datalen - 1] = (uint8_t)(crcVal & 0xFF);
return 0;
}
static uint8_t checkDataPEC(uint8_t* data, uint8_t len) {
static uint8_t checkDataPEC(const uint8_t* data, uint8_t len) {
if (len <= 2) { return 255; }
// Zero remainder means a valid CRC.
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() {
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;
@ -162,29 +140,28 @@ 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]);
//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 ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
}
}
}
}
calculateCommandPEC(args, 4);
@ -196,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;
@ -204,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();
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++) {
size_t offset = BUFFER_BMS_OFFSET(i, arglen);
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();
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
}

View File

@ -33,8 +33,8 @@ static bool buffer_message(const log_message_t* message) {
uint32_t* write_pos = using_buffer_2 ? &message_buffer.write_pos_2 : &message_buffer.write_pos;
/* Calculate buffer offset - buffer 2 starts at ISOTP_LOG_BUFFER_1_BYTES */
uint32_t buffer_offset = using_buffer_2 ? ISOTP_LOG_BUFFER_1_BYTES : 0;
uint32_t max_size = using_buffer_2 ? ISOTP_LOG_BUFFER_2_BYTES : ISOTP_LOG_BUFFER_1_BYTES; // Each buffer has its own size
const uint32_t buffer_offset = using_buffer_2 ? ISOTP_LOG_BUFFER_1_BYTES : 0;
const uint32_t max_size = using_buffer_2 ? ISOTP_LOG_BUFFER_2_BYTES : ISOTP_LOG_BUFFER_1_BYTES; // Each buffer has its own size
/* Check if we have enough space in the current buffer */
if (*write_pos + message->message_length > max_size) {
@ -54,7 +54,7 @@ static bool buffer_message(const log_message_t* message) {
/* Swap buffers and prepare for sending */
static bool swap_buffers_for_sending() {
/* Don't swap if there's nothing to send */
uint32_t* current_pos = using_buffer_2 ? &message_buffer.write_pos_2 : &message_buffer.write_pos;
const uint32_t* current_pos = using_buffer_2 ? &message_buffer.write_pos_2 : &message_buffer.write_pos;
if (*current_pos == 0) {
return false;
}
@ -102,7 +102,7 @@ void isotp_log_backend_init(void) {
isotp_backend_registered = log_register_backend(&isotp_backend);
/* Set up ISO-TP connection */
int status = isotp_add_connection(ISOTP_LOG_FC_CAN_ID, ISOTP_LOG_CAN_ID, ISOTP_FLAGS_NONE);
const int status = isotp_add_connection(ISOTP_LOG_FC_CAN_ID, ISOTP_LOG_CAN_ID, ISOTP_FLAGS_NONE);
if (status < 0) {
log_error("Failed to add ISO-TP connection: %s", isotp_status_to_string((isotp_status_t)status));
return;
@ -154,12 +154,12 @@ static void isotp_backend_flush(void) {
/* Swap buffers if we have data to send and we're ready to send */
if (streaming_enabled) {
/* Get the buffer that is currently being written to */
uint32_t buffer_offset = !using_buffer_2 ? 0 : ISOTP_LOG_BUFFER_1_BYTES;
uint32_t buffer_size = !using_buffer_2 ? message_buffer.write_pos : message_buffer.write_pos_2;
const uint32_t buffer_offset = !using_buffer_2 ? 0 : ISOTP_LOG_BUFFER_1_BYTES;
const uint32_t buffer_size = !using_buffer_2 ? message_buffer.write_pos : message_buffer.write_pos_2;
/* Only send if we have data and a previous send is not in progress */
if (buffer_size > 0) {
isotp_status_t status = isotp_add_message(
const isotp_status_t status = isotp_add_message(
isotp_connection_id,
message_buffer.buffer + buffer_offset,
buffer_size

View File

@ -11,7 +11,7 @@
#include "stm32h7xx_hal.h"
/* Backend management */
static const log_backend_t* registered_backends[MAX_LOG_BACKENDS] = {0};
static const log_backend_t* registered_backends[MAX_LOG_BACKENDS] = {};
static uint8_t backend_count = 0;
/* Register a new logging backend */

View File

@ -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

View File

@ -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"
@ -15,15 +17,7 @@
#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;
@ -46,7 +40,9 @@ static inline void update_error_window(bool error, int id) {
}
HAL_StatusTypeDef battery_init(SPI_HandleTypeDef *hspi) {
auto ret = AMS_Init(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() {
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));
@ -72,16 +82,16 @@ HAL_StatusTypeDef battery_update() {
if (ret.status == ADBMS_OVERVOLT || ret.status == ADBMS_UNDERVOLT) {
if (ret.bms_id != -1 && ret.bms_id < N_BMS) {
const char* error_type = (ret.status == ADBMS_OVERVOLT) ? "overvoltage" : "undervoltage";
uint32_t voltage_flags = (ret.status == ADBMS_OVERVOLT) ?
modules[ret.bms_id].overVoltage :
modules[ret.bms_id].underVoltage;
const uint32_t voltage_flags = (ret.status == ADBMS_OVERVOLT) ?
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]);
}
}
@ -97,69 +107,60 @@ 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++) {
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];
}
if (cellTemps[i][j] > module_temps[i].max) {
module_temps[i].max = cellTemps[i][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 (cellTemps[i][j] < module_temps[i].min) {
module_temps[i].min = cellTemps[i][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] > (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 {

View File

@ -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);
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,16 +49,16 @@ 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;
} else {
data[7] = 0xFF;
}
HAL_StatusTypeDef ret = ftcan_transmit(CAN_ID_AMS_STATUS, data, sizeof(data));
const HAL_StatusTypeDef ret = ftcan_transmit(CAN_ID_AMS_STATUS, data, sizeof(data));
if (ret != HAL_OK) {
return ret;
}
@ -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
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:
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));
}
@ -172,5 +172,7 @@ void ftcan_msg_received_cb(uint16_t id, size_t len, const uint8_t *data) {
case CAN_ID_AMS_IN:
ts_sm_handle_ams_in(data);
break;
default:
break;
}
}

View File

@ -32,7 +32,7 @@ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *handle) {
if (handle != htim || htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) {
return;
}
uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
const uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if (period == 0) {
// First edge, ignore
return;
@ -40,7 +40,7 @@ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *handle) {
imd_data.last_high = HAL_GetTick();
imd_data.freq = FREQ_TIMER / period;
uint32_t high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
const uint32_t high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
imd_data.duty_cycle = (100 * high_time) / period;
// Check PWM frequency for state determination

View File

@ -203,7 +203,7 @@ int main(void)
// for testing. in the final code can log streaming will be enabled by can message
isotp_log_enable_streaming(LOG_LEVEL_INFO);
shunt_init();
//shunt_init();
ts_sm_init();
soc_init();
imd_init(&htim15);
@ -242,7 +242,7 @@ int main(void)
print_battery_info();
print_master_status();
}
shunt_check();
//shunt_check();
ts_sm_update();
soc_update();
imd_update();
@ -331,17 +331,16 @@ void PeriphCommonClock_Config(void)
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_FDCAN;
PeriphClkInitStruct.PLL2.PLL2M = 1;
PeriphClkInitStruct.PLL2.PLL2N = 8;
PeriphClkInitStruct.PLL2.PLL2P = 3;
PeriphClkInitStruct.PLL2.PLL2Q = 3;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL3.PLL3M = 1;
PeriphClkInitStruct.PLL3.PLL3N = 8;
PeriphClkInitStruct.PLL3.PLL3P = 2;
PeriphClkInitStruct.PLL3.PLL3Q = 2;
PeriphClkInitStruct.PLL3.PLL3R = 3;
PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_3;
PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL3;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
@ -370,7 +369,7 @@ static void MX_ADC1_Init(void)
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_16B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
@ -437,7 +436,7 @@ static void MX_ADC2_Init(void)
/** Common config
*/
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc2.Init.Resolution = ADC_RESOLUTION_16B;
hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
@ -498,7 +497,7 @@ static void MX_FDCAN1_Init(void)
hfdcan1.Init.ProtocolException = DISABLE;
hfdcan1.Init.NominalPrescaler = 2;
hfdcan1.Init.NominalSyncJumpWidth = 1;
hfdcan1.Init.NominalTimeSeg1 = 31;
hfdcan1.Init.NominalTimeSeg1 = 23;
hfdcan1.Init.NominalTimeSeg2 = 8;
hfdcan1.Init.DataPrescaler = 1;
hfdcan1.Init.DataSyncJumpWidth = 1;

View File

@ -12,7 +12,7 @@
#include "swo_log_backend.h"
void print_master_status() {
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
}

View File

@ -8,7 +8,7 @@
#include "swo_log_backend.h"
void print_battery_info() {
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());
}

View File

@ -35,14 +35,14 @@ void shunt_handle_can_msg(uint16_t id, const uint8_t *data) {
shunt_data.last_message = HAL_GetTick();
// All result messages contain a big-endian 6-byte integer
uint64_t result = ftcan_unmarshal_unsigned(&data, 6);
const uint64_t result = ftcan_unmarshal_unsigned(&data, 6);
switch (id) {
case CAN_ID_SHUNT_CURRENT:
shunt_data.current = result;
if (shunt_data.last_current_message > 0) {
uint32_t now = HAL_GetTick();
float dt = (now - shunt_data.last_current_message) * 0.001f;
const uint32_t now = HAL_GetTick();
const float dt = (now - shunt_data.last_current_message) * 0.001f;
shunt_data.current_counter += shunt_data.current * dt;
}
shunt_data.last_current_message = HAL_GetTick();

View File

@ -12,54 +12,31 @@
#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() {
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
float as_delta = shunt_data.current_counter - mAs_before_current;
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;
size_t array_length = sizeof(OCV_SOC_PAIRS) / sizeof(*OCV_SOC_PAIRS);
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
while (i < array_length && OCV_SOC_PAIRS[i].ocv <= ocv) {
i++;
@ -78,13 +55,39 @@ float soc_for_ocv(uint16_t ocv) {
}
// Perform linear interpolation
uint16_t ocv1 = OCV_SOC_PAIRS[i - 1].ocv;
uint16_t ocv2 = OCV_SOC_PAIRS[i].ocv;
float soc1 = OCV_SOC_PAIRS[i - 1].soc;
float soc2 = OCV_SOC_PAIRS[i].soc;
const uint16_t ocv1 = OCV_SOC_PAIRS[i - 1].ocv;
const uint16_t ocv2 = OCV_SOC_PAIRS[i].ocv;
const float soc1 = OCV_SOC_PAIRS[i - 1].soc;
const float soc2 = OCV_SOC_PAIRS[i].soc;
float slope = (soc2 - soc1) / (ocv2 - ocv1);
float interpolated_soc = soc1 + slope * (ocv - ocv1);
const float slope = (soc2 - soc1) / (ocv2 - ocv1);
const float interpolated_soc = soc1 + slope * (ocv - ocv1);
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;
}
}

View File

@ -19,15 +19,15 @@ typedef enum {
uint32_t count = 0;
bool main_led = false;
LedColor led_1 = (LedColor) {.red = 0, .green = 0, .blue = 0};
LedColor led_2 = (LedColor) {.red = 0, .green = 0, .blue = 0};
auto led_1 = (LedColor) {.red = 0, .green = 0, .blue = 0};
auto led_2 = (LedColor) {.red = 0, .green = 0, .blue = 0};
#define ITER_COUNT 20
#define INTERN_ERROR_COUNT 7500
static void set_led_color_hw(LedColor color) {
GPIO_PinState red = color.red ? GPIO_PIN_RESET : GPIO_PIN_SET;
GPIO_PinState green = color.green ? GPIO_PIN_RESET : GPIO_PIN_SET;
GPIO_PinState blue = color.blue ? GPIO_PIN_RESET : GPIO_PIN_SET;
const GPIO_PinState red = color.red ? GPIO_PIN_RESET : GPIO_PIN_SET;
const GPIO_PinState green = color.green ? GPIO_PIN_RESET : GPIO_PIN_SET;
const GPIO_PinState blue = color.blue ? GPIO_PIN_RESET : GPIO_PIN_SET;
HAL_GPIO_WritePin(STATUS_LED_R_GPIO_Port, STATUS_LED_R_Pin, red);
HAL_GPIO_WritePin(STATUS_LED_G_GPIO_Port, STATUS_LED_G_Pin, green);

View File

@ -202,11 +202,30 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
void HAL_FDCAN_MspInit(FDCAN_HandleTypeDef* hfdcan)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(hfdcan->Instance==FDCAN1)
{
/* USER CODE BEGIN FDCAN1_MspInit 0 */
/* USER CODE END FDCAN1_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FDCAN;
PeriphClkInitStruct.PLL2.PLL2M = 1;
PeriphClkInitStruct.PLL2.PLL2N = 8;
PeriphClkInitStruct.PLL2.PLL2P = 2;
PeriphClkInitStruct.PLL2.PLL2Q = 4;
PeriphClkInitStruct.PLL2.PLL2R = 2;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0.0;
PeriphClkInitStruct.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Peripheral clock enable */
__HAL_RCC_FDCAN_CLK_ENABLE();

View File

@ -12,6 +12,7 @@
TSStateHandle ts_state;
static uint32_t precharge_95_reached_timestamp = 0;
static uint32_t precharge_start_timestamp = 0;
static uint32_t charging_check_timestamp = 0;
static uint32_t discharge_begin_timestamp = 0;
static uint32_t precharge_opened_timestamp = 0;
@ -27,7 +28,7 @@ void ts_sm_update() {
ts_state.current_state = TS_ERROR;
}
auto old_state = ts_state.current_state;
auto const old_state = ts_state.current_state;
switch (ts_state.current_state) {
case TS_INACTIVE:
@ -104,15 +105,20 @@ TSState ts_sm_update_precharge() {
discharge_begin_timestamp = HAL_GetTick();
return TS_DISCHARGE;
}
if (precharge_start_timestamp == 0) {
precharge_start_timestamp = HAL_GetTick();
}
if (shunt_data.voltage_veh > MIN_VEHICLE_SIDE_VOLTAGE &&
shunt_data.voltage_veh > 0.95 * shunt_data.voltage_bat) {
uint32_t now = HAL_GetTick();
const uint32_t now = HAL_GetTick();
if (precharge_95_reached_timestamp == 0) {
precharge_95_reached_timestamp = now;
} else if ((now - precharge_95_reached_timestamp) >=
PRECHARGE_95_DURATION) {
PRECHARGE_95_DURATION &&
(now - precharge_start_timestamp) >= PRECHARGE_MIN_DURATION) {
precharge_95_reached_timestamp = 0;
precharge_opened_timestamp = 0;
precharge_start_timestamp = 0;
//precharge_opened = 0;
return TS_ACTIVE;
}
@ -133,7 +139,7 @@ TSState ts_sm_update_discharge() {
TSState ts_sm_update_error() {
static uint32_t no_error_since = 0;
if (ts_state.error_source == 0) {
uint32_t now = HAL_GetTick();
const uint32_t now = HAL_GetTick();
if (no_error_since == 0) {
no_error_since = now;
} else if (now - no_error_since > NO_ERROR_TIME) {
@ -205,7 +211,7 @@ void ts_sm_set_relay_position(Relay relay, int closed) {
static int pos_closed = 0;
static int pre_closed = 0;
GPIO_PinState state = closed ? GPIO_PIN_SET : GPIO_PIN_RESET;
const GPIO_PinState state = closed ? GPIO_PIN_SET : GPIO_PIN_RESET;
switch (relay) {
case RELAY_NEG:
ts_sm_check_close_wait(&neg_closed, closed);
@ -227,7 +233,7 @@ void ts_sm_check_close_wait(int *is_closed, int should_close) {
if (should_close != *is_closed) {
*is_closed = should_close;
if (should_close) {
uint32_t dt = HAL_GetTick() - last_close_timestamp;
const uint32_t dt = HAL_GetTick() - last_close_timestamp;
if (dt < RELAY_CLOSE_WAIT) {
HAL_Delay(RELAY_CLOSE_WAIT - dt);
}

View File

@ -1,5 +1,5 @@
##########################################################################################################################
# File automatically-generated by tool: [projectgenerator] version: [4.5.0-RC5] date: [Wed Mar 26 19:38:08 CET 2025]
# File automatically-generated by tool: [projectgenerator] version: [4.6.0-B36] date: [Sat Apr 26 19:50:03 CEST 2025]
##########################################################################################################################
# ------------------------------------------------
@ -150,7 +150,7 @@ CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
# LDFLAGS
#######################################
# link script
LDSCRIPT = stm32h7a3ritx_flash.ld
LDSCRIPT = STM32H7A3XX_FLASH.ld
# libraries
LIBS = -lc -lm -lnosys

View File

@ -1,6 +1,7 @@
#MicroXplorer Configuration settings - do not modify
ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_10
ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,OffsetSignedSaturation-0\#ChannelRegularConversion,NbrOfConversionFlag,master
ADC1.ClockPrescaler=ADC_CLOCK_ASYNC_DIV2
ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,OffsetSignedSaturation-0\#ChannelRegularConversion,NbrOfConversionFlag,master,ClockPrescaler
ADC1.NbrOfConversionFlag=1
ADC1.OffsetNumber-0\#ChannelRegularConversion=ADC_OFFSET_NONE
ADC1.OffsetSignedSaturation-0\#ChannelRegularConversion=DISABLE
@ -8,7 +9,8 @@ ADC1.Rank-0\#ChannelRegularConversion=1
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.master=1
ADC2.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_10
ADC2.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,OffsetSignedSaturation-0\#ChannelRegularConversion,NbrOfConversionFlag,SingleDiff-0\#ChannelRegularConversion
ADC2.ClockPrescaler=ADC_CLOCK_ASYNC_DIV2
ADC2.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,OffsetSignedSaturation-0\#ChannelRegularConversion,NbrOfConversionFlag,SingleDiff-0\#ChannelRegularConversion,ClockPrescaler
ADC2.NbrOfConversionFlag=1
ADC2.OffsetNumber-0\#ChannelRegularConversion=ADC_OFFSET_NONE
ADC2.OffsetSignedSaturation-0\#ChannelRegularConversion=DISABLE
@ -18,12 +20,12 @@ ADC2.SingleDiff-0\#ChannelRegularConversion=ADC_DIFFERENTIAL_ENDED
CAD.formats=[]
CAD.pinconfig=Dual
CAD.provider=
FDCAN1.CalculateBaudRateNominal=533333
FDCAN1.CalculateTimeBitNominal=1875
FDCAN1.CalculateTimeQuantumNominal=46.875
FDCAN1.CalculateBaudRateNominal=500000
FDCAN1.CalculateTimeBitNominal=2000
FDCAN1.CalculateTimeQuantumNominal=62.5
FDCAN1.IPParameters=CalculateTimeQuantumNominal,CalculateTimeBitNominal,CalculateBaudRateNominal,StdFiltersNbr,NominalPrescaler,NominalTimeSeg1,NominalTimeSeg2,RxFifo0ElmtsNbr,TxFifoQueueElmtsNbr
FDCAN1.NominalPrescaler=2
FDCAN1.NominalTimeSeg1=31
FDCAN1.NominalTimeSeg1=23
FDCAN1.NominalTimeSeg2=8
FDCAN1.RxFifo0ElmtsNbr=16
FDCAN1.StdFiltersNbr=32
@ -265,6 +267,7 @@ PH1-OSC_OUT.Signal=RCC_OSC_OUT
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerLinker=GCC
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
@ -294,6 +297,7 @@ ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_FDCAN1_Init-FDCAN1-false-HAL-true,4-MX_TIM15_Init-TIM15-false-HAL-true,5-MX_SPI1_Init-SPI1-false-HAL-true,6-MX_SPI2_Init-SPI2-false-HAL-true,7-MX_ADC1_Init-ADC1-false-HAL-true,8-MX_ADC2_Init-ADC2-false-HAL-true,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true
RCC.ADCCLockSelection=RCC_ADCCLKSOURCE_PLL3
RCC.ADCFreq_Value=42666666.666666664
RCC.AHB12Freq_Value=64000000
RCC.AHB4Freq_Value=64000000
@ -315,21 +319,23 @@ RCC.DFSDMACLkFreq_Value=64000000
RCC.DFSDMFreq_Value=64000000
RCC.DIVM1=1
RCC.DIVM2=1
RCC.DIVM3=1
RCC.DIVN1=8
RCC.DIVN2=8
RCC.DIVN3=8
RCC.DIVP1Freq_Value=64000000
RCC.DIVP2=3
RCC.DIVP2Freq_Value=42666666.666666664
RCC.DIVP3Freq_Value=32250000
RCC.DIVP2Freq_Value=64000000
RCC.DIVP3Freq_Value=64000000
RCC.DIVQ1Freq_Value=64000000
RCC.DIVQ2=3
RCC.DIVQ2Freq_Value=42666666.666666664
RCC.DIVQ3Freq_Value=32250000
RCC.DIVQ2=4
RCC.DIVQ2Freq_Value=32000000
RCC.DIVQ3Freq_Value=64000000
RCC.DIVR1Freq_Value=64000000
RCC.DIVR2Freq_Value=64000000
RCC.DIVR3Freq_Value=32250000
RCC.DIVR3=3
RCC.DIVR3Freq_Value=42666666.666666664
RCC.FDCANCLockSelection=RCC_FDCANCLKSOURCE_PLL2
RCC.FDCANFreq_Value=42666666.666666664
RCC.FDCANFreq_Value=32000000
RCC.FMCFreq_Value=64000000
RCC.FamilyName=M
RCC.HCLK3ClockFreq_Value=64000000
@ -338,15 +344,16 @@ RCC.HSE_VALUE=16000000
RCC.I2C123CLockSelection=RCC_I2C123CLKSOURCE_CSI
RCC.I2C123Freq_Value=4000000
RCC.I2C4Freq_Value=64000000
RCC.IPParameters=ADCFreq_Value,AHB12Freq_Value,AHB4Freq_Value,APB1Freq_Value,APB2Freq_Value,APB3Freq_Value,APB4Freq_Value,AXIClockFreq_Value,CDCPREFreq_Value,CECFreq_Value,CKPERFreq_Value,CortexFreq_Value,CpuClockFreq_Value,DAC1Freq_Value,DAC2Freq_Value,DFSDM2ACLkFreq_Value,DFSDM2Freq_Value,DFSDMACLkFreq_Value,DFSDMFreq_Value,DIVM1,DIVM2,DIVN1,DIVN2,DIVP1Freq_Value,DIVP2,DIVP2Freq_Value,DIVP3Freq_Value,DIVQ1Freq_Value,DIVQ2,DIVQ2Freq_Value,DIVQ3Freq_Value,DIVR1Freq_Value,DIVR2Freq_Value,DIVR3Freq_Value,FDCANCLockSelection,FDCANFreq_Value,FMCFreq_Value,FamilyName,HCLK3ClockFreq_Value,HCLKFreq_Value,HSE_VALUE,I2C123CLockSelection,I2C123Freq_Value,I2C4Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPTIM345Freq_Value,LPUART1Freq_Value,LTDCFreq_Value,MCO1PinFreq_Value,MCO2PinFreq_Value,PLL2FRACN,PLLFRACN,PLLSourceVirtual,QSPIFreq_Value,RNGFreq_Value,RTCFreq_Value,SAI1Freq_Value,SAI2AFreq_Value,SAI2BFreq_Value,SDMMCFreq_Value,SPDIFRXFreq_Value,SPI123Freq_Value,SPI45Freq_Value,SPI6Freq_Value,SWPMI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,Tim1OutputFreq_Value,Tim2OutputFreq_Value,TraceFreq_Value,USART16Freq_Value,USART234578Freq_Value,USBFreq_Value,VCO1OutputFreq_Value,VCO2OutputFreq_Value,VCO3OutputFreq_Value,VCOInput1Freq_Value,VCOInput2Freq_Value,VCOInput3Freq_Value
RCC.IPParameters=ADCCLockSelection,ADCFreq_Value,AHB12Freq_Value,AHB4Freq_Value,APB1Freq_Value,APB2Freq_Value,APB3Freq_Value,APB4Freq_Value,AXIClockFreq_Value,CDCPREFreq_Value,CECFreq_Value,CKPERFreq_Value,CortexFreq_Value,CpuClockFreq_Value,DAC1Freq_Value,DAC2Freq_Value,DFSDM2ACLkFreq_Value,DFSDM2Freq_Value,DFSDMACLkFreq_Value,DFSDMFreq_Value,DIVM1,DIVM2,DIVM3,DIVN1,DIVN2,DIVN3,DIVP1Freq_Value,DIVP2Freq_Value,DIVP3Freq_Value,DIVQ1Freq_Value,DIVQ2,DIVQ2Freq_Value,DIVQ3Freq_Value,DIVR1Freq_Value,DIVR2Freq_Value,DIVR3,DIVR3Freq_Value,FDCANCLockSelection,FDCANFreq_Value,FMCFreq_Value,FamilyName,HCLK3ClockFreq_Value,HCLKFreq_Value,HSE_VALUE,I2C123CLockSelection,I2C123Freq_Value,I2C4Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPTIM345Freq_Value,LPUART1Freq_Value,LTDCFreq_Value,MCO1PinFreq_Value,MCO2PinFreq_Value,PLL2FRACN,PLL3FRACN,PLLFRACN,PLLSourceVirtual,QSPIFreq_Value,RNGFreq_Value,RTCFreq_Value,SAI1Freq_Value,SAI2AFreq_Value,SAI2BFreq_Value,SDMMCFreq_Value,SPDIFRXFreq_Value,SPI123Freq_Value,SPI45Freq_Value,SPI6Freq_Value,SWPMI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,Tim1OutputFreq_Value,Tim2OutputFreq_Value,TraceFreq_Value,USART16Freq_Value,USART234578Freq_Value,USBFreq_Value,VCO1OutputFreq_Value,VCO2OutputFreq_Value,VCO3OutputFreq_Value,VCOInput1Freq_Value,VCOInput2Freq_Value,VCOInput3Freq_Value
RCC.LPTIM1Freq_Value=64000000
RCC.LPTIM2Freq_Value=64000000
RCC.LPTIM345Freq_Value=64000000
RCC.LPUART1Freq_Value=64000000
RCC.LTDCFreq_Value=32250000
RCC.LTDCFreq_Value=42666666.666666664
RCC.MCO1PinFreq_Value=64000000
RCC.MCO2PinFreq_Value=64000000
RCC.PLL2FRACN=0
RCC.PLL3FRACN=0
RCC.PLLFRACN=0
RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSE
RCC.QSPIFreq_Value=64000000
@ -371,10 +378,10 @@ RCC.USART234578Freq_Value=64000000
RCC.USBFreq_Value=64000000
RCC.VCO1OutputFreq_Value=128000000
RCC.VCO2OutputFreq_Value=128000000
RCC.VCO3OutputFreq_Value=64500000
RCC.VCO3OutputFreq_Value=128000000
RCC.VCOInput1Freq_Value=16000000
RCC.VCOInput2Freq_Value=16000000
RCC.VCOInput3Freq_Value=500000
RCC.VCOInput3Freq_Value=16000000
SH.ADCx_INP10.0=ADC1_INP10,IN10-Single-Ended
SH.ADCx_INP10.1=ADC2_INP10,IN10-Differential
SH.ADCx_INP10.ConfNb=2

View File

@ -0,0 +1,209 @@
/*
******************************************************************************
**
** File : LinkerScript.ld
**
** Author : STM32CubeMX
**
** Abstract : Linker script for STM32H7A3RITx series
** 2048Kbytes FLASH and 1216Kbytes RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed “as is,” without any warranty
** of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>&copy; COPYRIGHT(c) 2025 STMicroelectronics</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
** 1. Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
** 3. Neither the name of STMicroelectronics nor the names of its contributors
** may be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM); /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 1024K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(4);
} >FLASH
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >DTCMRAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >DTCMRAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >DTCMRAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}

View File

@ -0,0 +1,745 @@
/**
******************************************************************************
* @file startup_stm32h7a3xx.s
* @author MCD Application Team
* @brief STM32H7B3xx Devices vector table for GCC based toolchain.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* Copyright (c) 2019 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
.syntax unified
.cpu cortex-m7
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* set stack pointer */
/* Call the ExitRun0Mode function to configure the power supply */
bl ExitRun0Mode
/* Call the clock system initialization function.*/
bl SystemInit
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
movs r3, #0
b LoopCopyDataInit
CopyDataInit:
ldr r4, [r2, r3]
str r4, [r0, r3]
adds r3, r3, #4
LoopCopyDataInit:
adds r4, r0, r3
cmp r4, r1
bcc CopyDataInit
/* Zero fill the bss segment. */
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
FillZerobss:
str r3, [r2]
adds r2, r2, #4
LoopFillZerobss:
cmp r2, r4
bcc FillZerobss
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
* @param None
* @retval None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_PVM_IRQHandler /* PVD/PVM through EXTI Line detection */
.word RTC_TAMP_STAMP_CSS_LSE_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word FDCAN1_IT0_IRQHandler /* FDCAN1 interrupt line 0 */
.word FDCAN2_IT0_IRQHandler /* FDCAN2 interrupt line 0 */
.word FDCAN1_IT1_IRQHandler /* FDCAN1 interrupt line 1 */
.word FDCAN2_IT1_IRQHandler /* FDCAN2 interrupt line 1 */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_IRQHandler /* TIM1 Break interrupt */
.word TIM1_UP_IRQHandler /* TIM1 Update interrupt */
.word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation interrupt */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word DFSDM2_IRQHandler /* DFSDM2 Interrupt */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FMC_IRQHandler /* FMC */
.word SDMMC1_IRQHandler /* SDMMC1 */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word FDCAN_CAL_IRQHandler /* FDCAN calibration unit interrupt*/
.word DFSDM1_FLT4_IRQHandler /* DFSDM Filter4 Interrupt */
.word DFSDM1_FLT5_IRQHandler /* DFSDM Filter5 Interrupt */
.word DFSDM1_FLT6_IRQHandler /* DFSDM Filter6 Interrupt */
.word DFSDM1_FLT7_IRQHandler /* DFSDM Filter7 Interrupt */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word DCMI_PSSI_IRQHandler /* DCMI, PSSI */
.word 0 /* Reserved */
.word RNG_IRQHandler /* RNG */
.word FPU_IRQHandler /* FPU */
.word UART7_IRQHandler /* UART7 */
.word UART8_IRQHandler /* UART8 */
.word SPI4_IRQHandler /* SPI4 */
.word SPI5_IRQHandler /* SPI5 */
.word SPI6_IRQHandler /* SPI6 */
.word SAI1_IRQHandler /* SAI1 */
.word LTDC_IRQHandler /* LTDC */
.word LTDC_ER_IRQHandler /* LTDC error */
.word DMA2D_IRQHandler /* DMA2D */
.word SAI2_IRQHandler /* SAI2 */
.word OCTOSPI1_IRQHandler /* OCTOSPI1 */
.word LPTIM1_IRQHandler /* LPTIM1 */
.word CEC_IRQHandler /* HDMI_CEC */
.word I2C4_EV_IRQHandler /* I2C4 Event */
.word I2C4_ER_IRQHandler /* I2C4 Error */
.word SPDIF_RX_IRQHandler /* SPDIF_RX */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word DMAMUX1_OVR_IRQHandler /* DMAMUX1 Overrun interrupt */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word DFSDM1_FLT0_IRQHandler /* DFSDM Filter0 Interrupt */
.word DFSDM1_FLT1_IRQHandler /* DFSDM Filter1 Interrupt */
.word DFSDM1_FLT2_IRQHandler /* DFSDM Filter2 Interrupt */
.word DFSDM1_FLT3_IRQHandler /* DFSDM Filter3 Interrupt */
.word 0 /* Reserved */
.word SWPMI1_IRQHandler /* Serial Wire Interface 1 global interrupt */
.word TIM15_IRQHandler /* TIM15 global Interrupt */
.word TIM16_IRQHandler /* TIM16 global Interrupt */
.word TIM17_IRQHandler /* TIM17 global Interrupt */
.word MDIOS_WKUP_IRQHandler /* MDIOS Wakeup Interrupt */
.word MDIOS_IRQHandler /* MDIOS global Interrupt */
.word JPEG_IRQHandler /* JPEG global Interrupt */
.word MDMA_IRQHandler /* MDMA global Interrupt */
.word 0 /* Reserved */
.word SDMMC2_IRQHandler /* SDMMC2 global Interrupt */
.word HSEM1_IRQHandler /* HSEM1 global Interrupt */
.word 0 /* Reserved */
.word DAC2_IRQHandler /* DAC2 global Interrupt */
.word DMAMUX2_OVR_IRQHandler /* DMAMUX Overrun interrupt */
.word BDMA2_Channel0_IRQHandler /* BDMA2 Channel 0 global Interrupt */
.word BDMA2_Channel1_IRQHandler /* BDMA2 Channel 1 global Interrupt */
.word BDMA2_Channel2_IRQHandler /* BDMA2 Channel 2 global Interrupt */
.word BDMA2_Channel3_IRQHandler /* BDMA2 Channel 3 global Interrupt */
.word BDMA2_Channel4_IRQHandler /* BDMA2 Channel 4 global Interrupt */
.word BDMA2_Channel5_IRQHandler /* BDMA2 Channel 5 global Interrupt */
.word BDMA2_Channel6_IRQHandler /* BDMA2 Channel 6 global Interrupt */
.word BDMA2_Channel7_IRQHandler /* BDMA2 Channel 7 global Interrupt */
.word COMP_IRQHandler /* COMP global Interrupt */
.word LPTIM2_IRQHandler /* LP TIM2 global interrupt */
.word LPTIM3_IRQHandler /* LP TIM3 global interrupt */
.word UART9_IRQHandler /* UART9 global interrupt */
.word USART10_IRQHandler /* USART10 global interrupt */
.word LPUART1_IRQHandler /* LP UART1 interrupt */
.word 0 /* Reserved */
.word CRS_IRQHandler /* Clock Recovery Global Interrupt */
.word ECC_IRQHandler /* ECC diagnostic Global Interrupt */
.word 0 /* Reserved */
.word DTS_IRQHandler /* DTS */
.word 0 /* Reserved */
.word WAKEUP_PIN_IRQHandler /* Interrupt for all 6 wake-up pins */
.word OCTOSPI2_IRQHandler /* OCTOSPI2 */
.word 0 /* Reserved */
.word 0 /* Reserved */
.word GFXMMU_IRQHandler /* GFXMMU */
.word BDMA1_IRQHandler /* BDMA1 */
.size g_pfnVectors, .-g_pfnVectors
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_PVM_IRQHandler
.thumb_set PVD_PVM_IRQHandler,Default_Handler
.weak RTC_TAMP_STAMP_CSS_LSE_IRQHandler
.thumb_set RTC_TAMP_STAMP_CSS_LSE_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Stream0_IRQHandler
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
.weak DMA1_Stream1_IRQHandler
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
.weak DMA1_Stream2_IRQHandler
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
.weak DMA1_Stream3_IRQHandler
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
.weak DMA1_Stream4_IRQHandler
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
.weak DMA1_Stream5_IRQHandler
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
.weak DMA1_Stream6_IRQHandler
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak FDCAN1_IT0_IRQHandler
.thumb_set FDCAN1_IT0_IRQHandler,Default_Handler
.weak FDCAN2_IT0_IRQHandler
.thumb_set FDCAN2_IT0_IRQHandler,Default_Handler
.weak FDCAN1_IT1_IRQHandler
.thumb_set FDCAN1_IT1_IRQHandler,Default_Handler
.weak FDCAN2_IT1_IRQHandler
.thumb_set FDCAN2_IT1_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_IRQHandler
.thumb_set TIM1_BRK_IRQHandler,Default_Handler
.weak TIM1_UP_IRQHandler
.thumb_set TIM1_UP_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_IRQHandler
.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak DFSDM2_IRQHandler
.thumb_set DFSDM2_IRQHandler,Default_Handler
.weak TIM8_BRK_TIM12_IRQHandler
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
.weak TIM8_UP_TIM13_IRQHandler
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
.weak TIM8_TRG_COM_TIM14_IRQHandler
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
.weak TIM8_CC_IRQHandler
.thumb_set TIM8_CC_IRQHandler,Default_Handler
.weak DMA1_Stream7_IRQHandler
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
.weak FMC_IRQHandler
.thumb_set FMC_IRQHandler,Default_Handler
.weak SDMMC1_IRQHandler
.thumb_set SDMMC1_IRQHandler,Default_Handler
.weak TIM5_IRQHandler
.thumb_set TIM5_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak UART5_IRQHandler
.thumb_set UART5_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak TIM7_IRQHandler
.thumb_set TIM7_IRQHandler,Default_Handler
.weak DMA2_Stream0_IRQHandler
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
.weak DMA2_Stream1_IRQHandler
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
.weak DMA2_Stream2_IRQHandler
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
.weak DMA2_Stream3_IRQHandler
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
.weak DMA2_Stream4_IRQHandler
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
.weak FDCAN_CAL_IRQHandler
.thumb_set FDCAN_CAL_IRQHandler,Default_Handler
.weak DFSDM1_FLT4_IRQHandler
.thumb_set DFSDM1_FLT4_IRQHandler,Default_Handler
.weak DFSDM1_FLT5_IRQHandler
.thumb_set DFSDM1_FLT5_IRQHandler,Default_Handler
.weak DFSDM1_FLT6_IRQHandler
.thumb_set DFSDM1_FLT6_IRQHandler,Default_Handler
.weak DFSDM1_FLT7_IRQHandler
.thumb_set DFSDM1_FLT7_IRQHandler,Default_Handler
.weak DMA2_Stream5_IRQHandler
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
.weak DMA2_Stream6_IRQHandler
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
.weak DMA2_Stream7_IRQHandler
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
.weak USART6_IRQHandler
.thumb_set USART6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak OTG_HS_EP1_OUT_IRQHandler
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
.weak OTG_HS_EP1_IN_IRQHandler
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
.weak OTG_HS_WKUP_IRQHandler
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
.weak OTG_HS_IRQHandler
.thumb_set OTG_HS_IRQHandler,Default_Handler
.weak DCMI_PSSI_IRQHandler
.thumb_set DCMI_PSSI_IRQHandler,Default_Handler
.weak RNG_IRQHandler
.thumb_set RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
.weak UART7_IRQHandler
.thumb_set UART7_IRQHandler,Default_Handler
.weak UART8_IRQHandler
.thumb_set UART8_IRQHandler,Default_Handler
.weak SPI4_IRQHandler
.thumb_set SPI4_IRQHandler,Default_Handler
.weak SPI5_IRQHandler
.thumb_set SPI5_IRQHandler,Default_Handler
.weak SPI6_IRQHandler
.thumb_set SPI6_IRQHandler,Default_Handler
.weak SAI1_IRQHandler
.thumb_set SAI1_IRQHandler,Default_Handler
.weak LTDC_IRQHandler
.thumb_set LTDC_IRQHandler,Default_Handler
.weak LTDC_ER_IRQHandler
.thumb_set LTDC_ER_IRQHandler,Default_Handler
.weak DMA2D_IRQHandler
.thumb_set DMA2D_IRQHandler,Default_Handler
.weak SAI2_IRQHandler
.thumb_set SAI2_IRQHandler,Default_Handler
.weak OCTOSPI1_IRQHandler
.thumb_set OCTOSPI1_IRQHandler,Default_Handler
.weak LPTIM1_IRQHandler
.thumb_set LPTIM1_IRQHandler,Default_Handler
.weak CEC_IRQHandler
.thumb_set CEC_IRQHandler,Default_Handler
.weak I2C4_EV_IRQHandler
.thumb_set I2C4_EV_IRQHandler,Default_Handler
.weak I2C4_ER_IRQHandler
.thumb_set I2C4_ER_IRQHandler,Default_Handler
.weak SPDIF_RX_IRQHandler
.thumb_set SPDIF_RX_IRQHandler,Default_Handler
.weak DMAMUX1_OVR_IRQHandler
.thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler
.weak DFSDM1_FLT0_IRQHandler
.thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler
.weak DFSDM1_FLT1_IRQHandler
.thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler
.weak DFSDM1_FLT2_IRQHandler
.thumb_set DFSDM1_FLT2_IRQHandler,Default_Handler
.weak DFSDM1_FLT3_IRQHandler
.thumb_set DFSDM1_FLT3_IRQHandler,Default_Handler
.weak SWPMI1_IRQHandler
.thumb_set SWPMI1_IRQHandler,Default_Handler
.weak TIM15_IRQHandler
.thumb_set TIM15_IRQHandler,Default_Handler
.weak TIM16_IRQHandler
.thumb_set TIM16_IRQHandler,Default_Handler
.weak TIM17_IRQHandler
.thumb_set TIM17_IRQHandler,Default_Handler
.weak MDIOS_WKUP_IRQHandler
.thumb_set MDIOS_WKUP_IRQHandler,Default_Handler
.weak MDIOS_IRQHandler
.thumb_set MDIOS_IRQHandler,Default_Handler
.weak JPEG_IRQHandler
.thumb_set JPEG_IRQHandler,Default_Handler
.weak MDMA_IRQHandler
.thumb_set MDMA_IRQHandler,Default_Handler
.weak SDMMC2_IRQHandler
.thumb_set SDMMC2_IRQHandler,Default_Handler
.weak HSEM1_IRQHandler
.thumb_set HSEM1_IRQHandler,Default_Handler
.weak DAC2_IRQHandler
.thumb_set DAC2_IRQHandler,Default_Handler
.weak DMAMUX2_OVR_IRQHandler
.thumb_set DMAMUX2_OVR_IRQHandler,Default_Handler
.weak BDMA2_Channel0_IRQHandler
.thumb_set BDMA2_Channel0_IRQHandler,Default_Handler
.weak BDMA2_Channel1_IRQHandler
.thumb_set BDMA2_Channel1_IRQHandler,Default_Handler
.weak BDMA2_Channel2_IRQHandler
.thumb_set BDMA2_Channel2_IRQHandler,Default_Handler
.weak BDMA2_Channel3_IRQHandler
.thumb_set BDMA2_Channel3_IRQHandler,Default_Handler
.weak BDMA2_Channel4_IRQHandler
.thumb_set BDMA2_Channel4_IRQHandler,Default_Handler
.weak BDMA2_Channel5_IRQHandler
.thumb_set BDMA2_Channel5_IRQHandler,Default_Handler
.weak BDMA2_Channel6_IRQHandler
.thumb_set BDMA2_Channel6_IRQHandler,Default_Handler
.weak BDMA2_Channel7_IRQHandler
.thumb_set BDMA2_Channel7_IRQHandler,Default_Handler
.weak COMP_IRQHandler
.thumb_set COMP_IRQHandler,Default_Handler
.weak LPTIM2_IRQHandler
.thumb_set LPTIM2_IRQHandler,Default_Handler
.weak LPTIM3_IRQHandler
.thumb_set LPTIM3_IRQHandler,Default_Handler
.weak LPTIM4_IRQHandler
.thumb_set LPTIM4_IRQHandler,Default_Handler
.weak LPTIM5_IRQHandler
.thumb_set LPTIM5_IRQHandler,Default_Handler
.weak UART9_IRQHandler
.thumb_set UART9_IRQHandler,Default_Handler
.weak USART10_IRQHandler
.thumb_set USART10_IRQHandler,Default_Handler
.weak LPUART1_IRQHandler
.thumb_set LPUART1_IRQHandler,Default_Handler
.weak CRS_IRQHandler
.thumb_set CRS_IRQHandler,Default_Handler
.weak ECC_IRQHandler
.thumb_set ECC_IRQHandler,Default_Handler
.weak DTS_IRQHandler
.thumb_set DTS_IRQHandler,Default_Handler
.weak WAKEUP_PIN_IRQHandler
.thumb_set WAKEUP_PIN_IRQHandler,Default_Handler
.weak OCTOSPI2_IRQHandler
.thumb_set OCTOSPI2_IRQHandler,Default_Handler
.weak GFXMMU_IRQHandler
.thumb_set GFXMMU_IRQHandler,Default_Handler
.weak BDMA1_IRQHandler
.thumb_set BDMA1_IRQHandler,Default_Handler