Compare commits

...

10 Commits

11 changed files with 100 additions and 74 deletions

View File

@ -40,7 +40,6 @@
"request": "attach", "request": "attach",
"type": "cortex-debug", "type": "cortex-debug",
"servertype": "openocd", "servertype": "openocd",
"preLaunchTask": "Build STM",
"device": "stm32h7a3xxq.s", "device": "stm32h7a3xxq.s",
"configFiles": [ "configFiles": [
"openocd.cfg" "openocd.cfg"

View File

@ -1,4 +1,5 @@
#include <math.h> #include <math.h>
#include <stdint.h>
#define USE_CALC #define USE_CALC
@ -18,10 +19,10 @@
// With R_T/R_0 and R_0 = R_T@25C // With R_T/R_0 and R_0 = R_T@25C
// R_T/R_0 = 1 / V_REF / ADC - 1 // R_T/R_0 = 1 / V_REF / ADC - 1
[[gnu::optimize("fast-math")]]
static inline uint16_t ntc_mv_to_celsius(int16_t adc) { static inline uint16_t ntc_mv_to_celsius(int16_t adc) {
float log_ohms = logf(1/((VREF/adc)-1)); 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); 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 #else
// Lookup Table coming soon; not really needed but fun? // Lookup Table coming soon; not really needed but fun?

View File

@ -4,13 +4,17 @@
#include "main.h" #include "main.h"
#define N_BMS 1 #define N_BMS 1
#define ADBMS_SPI_TIMEOUT 100 // Timeout in ms #define N_CELLS 16
#define ADBMS_MAX_CHIP_TEMP 110 // max temperature of ADBMS6830B (not battery) in C
#define ADBMS_SPI_TIMEOUT 50 // Timeout in ms
[[maybe_unused]] static inline void mcuAdbmsCSLow() { [[maybe_unused, gnu::always_inline]]
static inline void mcuAdbmsCSLow() {
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET);
} }
[[maybe_unused]] static inline void mcuAdbmsCSHigh() { [[maybe_unused, gnu::always_inline]]
static inline void mcuAdbmsCSHigh() {
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET);
} }

View File

@ -93,8 +93,7 @@ static inline void __swo_print(unsigned int channel, const char *str) {
} }
} }
[[maybe_unused]] static inline void debug_clear_console() {
static void debug_clear_console() {
for (int i = 0; i < LOG_LEVEL_NOISY; i++) { for (int i = 0; i < LOG_LEVEL_NOISY; i++) {
#if USE_ANSI_ESCAPE_CODES #if USE_ANSI_ESCAPE_CODES
__swo_print(i, "\033[2J\033[;H"); // clear screen __swo_print(i, "\033[2J\033[;H"); // clear screen
@ -104,44 +103,58 @@ static void debug_clear_console() {
} }
} }
#define debug_log(level, msg, ...) \ [[gnu::format(printf, 2, 3)]]
do { \ static inline void debug_log(enum log_level_t level, const char *msg, ...) {
if (DEBUG_CHANNEL_ENABLED(level)) { \ if (!DEBUG_CHANNEL_ENABLED(level)) {
char __swo_buffer[MAX_MESSAGE_LENGTH]; \ return;
size_t len = \ }
snprintf(__swo_buffer, sizeof(__swo_buffer), msg, ##__VA_ARGS__); \
__swo_putc('\n', level); \ char __swo_buffer[MAX_MESSAGE_LENGTH];
/* Print timestamp if enabled */ \ va_list args;
if (PRINT_TIMESTAMP) { \ va_start(args, msg);
char __time_buffer[16]; \ size_t len = vsnprintf(__swo_buffer, sizeof(__swo_buffer), msg, args);
if (USE_ANSI_ESCAPE_CODES) { \ va_end(args);
snprintf(__time_buffer, sizeof(__time_buffer), \
"\033[90m[%lu]\033[0m ", HAL_GetTick()); \ __swo_putc('\n', level);
} else { \
snprintf(__time_buffer, sizeof(__time_buffer), "[%lu] ", \ /* Print timestamp if enabled */
HAL_GetTick()); \ if (PRINT_TIMESTAMP) {
} \ char __time_buffer[16];
__swo_print(level, __time_buffer); \ if (USE_ANSI_ESCAPE_CODES) {
} \ snprintf(__time_buffer, sizeof(__time_buffer),
__swo_print(level, log_level_names[level]); \ "\033[90m[%lu]\033[0m ", HAL_GetTick());
__swo_print(level, __swo_buffer); \ } else {
if (len >= sizeof(__swo_buffer)) { \ snprintf(__time_buffer, sizeof(__time_buffer), "[%lu] ",
__swo_print(level, " [message length exceeded] "); \ HAL_GetTick());
} \ }
} \ __swo_print(level, __time_buffer);
} while (0) }
__swo_print(level, log_level_names[level]);
__swo_print(level, __swo_buffer);
if (len >= sizeof(__swo_buffer)) {
__swo_print(level, " [message length exceeded] ");
}
}
#define debug_log_cont(level, msg, ...) \ [[gnu::format(printf, 2, 3)]]
do { \ static inline void debug_log_cont(enum log_level_t level, const char *msg, ...) {
if (DEBUG_CHANNEL_ENABLED(level)) { \ if (!DEBUG_CHANNEL_ENABLED(level)) {
char __swo_buffer[MAX_MESSAGE_LENGTH]; \ return;
size_t len = \ }
snprintf(__swo_buffer, sizeof(__swo_buffer), msg, ##__VA_ARGS__); \
__swo_print(level, __swo_buffer); \ char __swo_buffer[MAX_MESSAGE_LENGTH];
if (len >= sizeof(__swo_buffer)) { \ va_list args;
__swo_print(level, " [message length exceeded] "); \ va_start(args, msg);
} \ size_t len = vsnprintf(__swo_buffer, sizeof(__swo_buffer), msg, args);
} \ va_end(args);
} while (0)
__swo_print(level, __swo_buffer);
if (len >= sizeof(__swo_buffer)) {
__swo_print(level, " [message length exceeded] ");
}
}
#endif /* __SWO_LOG_H */ #endif /* __SWO_LOG_H */

View File

@ -8,10 +8,7 @@
#ifndef INC_ADBMS_ABSTRACTION_H_ #ifndef INC_ADBMS_ABSTRACTION_H_
#define INC_ADBMS_ABSTRACTION_H_ #define INC_ADBMS_ABSTRACTION_H_
#include "ADBMS_CMD_MAKROS.h"
#include "ADBMS_Driver.h" #include "ADBMS_Driver.h"
#include "ADBMS_LL_Driver.h"
#include "main.h"
#define mV_from_ADBMS6830(x) (((((int16_t)(x))) * 0.150) + 1500) #define mV_from_ADBMS6830(x) (((((int16_t)(x))) * 0.150) + 1500)

View File

@ -83,7 +83,7 @@ typedef struct {
uint32_t bmsID; uint32_t bmsID;
struct ADBMS6830_Internal_Status status; struct ADBMS6830_Internal_Status status;
uint16_t internalDieTemp; int16_t internalDieTemp;
uint16_t analogSupplyVoltage; uint16_t analogSupplyVoltage;
uint16_t digitalSupplyVoltage; uint16_t digitalSupplyVoltage;
uint16_t sumOfCellMeasurements; uint16_t sumOfCellMeasurements;

View File

@ -30,6 +30,4 @@ extern Cell_Module modules[N_BMS];
extern uint32_t balancedCells; extern uint32_t balancedCells;
extern bool BalancingActive; extern bool BalancingActive;
extern uint8_t numberofCells;
#endif /* INC_AMS_HIGHLEVEL_H_ */ #endif /* INC_AMS_HIGHLEVEL_H_ */

View File

@ -9,9 +9,7 @@
#define ADBMS_LL_DRIVER_H_ #define ADBMS_LL_DRIVER_H_
#include "config_ADBMS6830.h" #include "config_ADBMS6830.h"
#include "stm32h7xx_hal.h"
#include <stdint.h> #include <stdint.h>
#define TARGET_STM32
uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi); uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi);

View File

@ -13,8 +13,6 @@
#include <stddef.h> #include <stddef.h>
#include "NTC.h" #include "NTC.h"
extern uint8_t numberofCells;
static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"}; static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
#define CHECK_RETURN(x) \ #define CHECK_RETURN(x) \
@ -91,8 +89,7 @@ HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi) {
} }
HAL_StatusTypeDef amsWakeUp() { HAL_StatusTypeDef amsWakeUp() {
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_A_SIZE)] = {0}; return __pollCMD(PLADC, 100); //wake up ADBMS6830, wait for T_wake = 200 us (100 cycles at 500 kHz)
return readCMD(RDCFGA, buffer, CFG_GROUP_A_SIZE);
} }
HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) { HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) {
@ -264,8 +261,7 @@ HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
(*module)[i].overVoltage = 0; (*module)[i].overVoltage = 0;
(*module)[i].underVoltage = 0; (*module)[i].underVoltage = 0;
for (size_t j = 0; j < numberofCells; for (size_t j = 0; j < N_CELLS; j++) { // ov/uv flags are 1-bit flags for each cell C0UV, C0OV, C1UV, C1OV, ...
j++) { // ov/uv flags are 1-bit flags for each cell C0UV, C0OV, C1UV, C1OV, ...
(*module)[i].underVoltage |= (ov_uv_data >> (j * 2)) & 0x01; (*module)[i].underVoltage |= (ov_uv_data >> (j * 2)) & 0x01;
(*module)[i].overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01; (*module)[i].overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01;
} }

View File

@ -18,8 +18,6 @@ Cell_Module modules[N_BMS] = {};
uint32_t balancedCells = 0; uint32_t balancedCells = 0;
bool balancingActive = false; bool balancingActive = false;
uint8_t numberofCells = 16;
uint8_t packetChecksumFails = 0; uint8_t packetChecksumFails = 0;
#define MAX_PACKET_CHECKSUM_FAILS 5 #define MAX_PACKET_CHECKSUM_FAILS 5
@ -37,7 +35,7 @@ static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_NO_ERROR};
ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) { 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, debug_log(LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
numberofCells); N_CELLS);
if (initAMS(hspi) != HAL_OK) { if (initAMS(hspi) != HAL_OK) {
debug_log(LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed"); debug_log(LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, -1}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, -1};
@ -106,8 +104,7 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match - 1}; return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match - 1};
} }
// TODO: replace with the correct threshold for the internal die temperature if ((match = any(module.internalDieTemp > ADBMS_MAX_CHIP_TEMP || module.status.THSD))) {
if ((match = any(module.internalDieTemp > 28000 || module.status.THSD))) {
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match - 1}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match - 1};
} }

View File

@ -15,6 +15,13 @@
#define INITIAL_COMMAND_PEC 0x0010 #define INITIAL_COMMAND_PEC 0x0010
#define INITIAL_DATA_PEC 0x0010 #define INITIAL_DATA_PEC 0x0010
// CRC polynomial constants
#define CRC15_POLY 0xC599
#define CRC10_POLY 0x8F
#define CRC15_REMAINDER_MASK 0x4000
#define CRC10_REMAINDER_MASK 0x200
#define CRC10_RESULT_MASK 0x3FF
SPI_HandleTypeDef* adbmsspi; SPI_HandleTypeDef* adbmsspi;
uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi) { uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi) {
@ -49,8 +56,8 @@ static uint16_t computeCRC15(const uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
remainder ^= (data[i] << 7); remainder ^= (data[i] << 7);
for (int b = 0; b < 8; b++) { for (int b = 0; b < 8; b++) {
if (remainder & 0x4000) { if (remainder & CRC15_REMAINDER_MASK) {
remainder = (uint16_t)((remainder << 1) ^ 0xC599); remainder = (uint16_t)((remainder << 1) ^ CRC15_POLY);
} else { } else {
remainder <<= 1; remainder <<= 1;
} }
@ -81,13 +88,12 @@ static uint8_t checkCommandPEC(uint8_t* data, uint8_t datalen) {
static uint16_t computeCRC10(const uint8_t* data, size_t length, bool rx_cmd) { static uint16_t computeCRC10(const uint8_t* data, size_t length, bool rx_cmd) {
uint16_t remainder = INITIAL_DATA_PEC; uint16_t remainder = INITIAL_DATA_PEC;
const uint16_t poly = 0x8F;
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
remainder ^= (uint16_t)(data[i] << 2); remainder ^= (uint16_t)(data[i] << 2);
for (int b = 0; b < 8; b++) { for (int b = 0; b < 8; b++) {
if (remainder & 0x200) { if (remainder & CRC10_REMAINDER_MASK) {
remainder = (uint16_t)((remainder << 1) ^ poly); remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
} else { } else {
remainder <<= 1; remainder <<= 1;
} }
@ -98,14 +104,14 @@ static uint16_t computeCRC10(const uint8_t* data, size_t length, bool rx_cmd) {
if (rx_cmd) { if (rx_cmd) {
remainder ^= (uint16_t)((data[length] & 0xFC) << 2); remainder ^= (uint16_t)((data[length] & 0xFC) << 2);
for (int b = 0; b < 6; b++) { for (int b = 0; b < 6; b++) {
if (remainder & 0x200) { if (remainder & CRC10_REMAINDER_MASK) {
remainder = (uint16_t)((remainder << 1) ^ poly); remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
} else { } else {
remainder <<= 1; remainder <<= 1;
} }
} }
} }
return (uint16_t)(remainder & 0x3FF); return (uint16_t)(remainder & CRC10_RESULT_MASK);
} }
static uint8_t calculateDataPEC(uint8_t* data, uint8_t datalen) { static uint8_t calculateDataPEC(uint8_t* data, uint8_t datalen) {
@ -242,10 +248,27 @@ HAL_StatusTypeDef ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen)
} }
} }
if (arglen == 0) {
return HAL_OK; //no data to check
}
//check data PEC //check data PEC
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, arglen); size_t offset = BUFFER_BMS_OFFSET(i, arglen);
if (checkDataPEC(&buffer[offset], arglen + 2) != 0) { 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: ");
for (size_t j = 0; j < arglen + 2; j++) {
debug_log_cont(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 ^");
//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(LOG_LEVEL_ERROR, "^^ ");
debug_log_cont(LOG_LEVEL_ERROR, " PEC ^");
return HAL_ERROR; return HAL_ERROR;
} }
} }
@ -271,5 +294,5 @@ HAL_StatusTypeDef __pollCMD(uint16_t command, uint8_t waitTime) {
return status; return status;
} }
return ((buffer[4 + (N_BMS * 2)] & 0x0F) == 0x0) ? HAL_BUSY : HAL_OK; //SDO goes high when data is ready return ((buffer[sizeof buffer] & 0x0F) == 0x0) ? HAL_BUSY : HAL_OK; //SDO goes high when data is ready
} }