diff --git a/Makefile b/Makefile index ab76e7fa..0c495d57 100644 --- a/Makefile +++ b/Makefile @@ -139,7 +139,8 @@ CSRC = $(ALLCSRC) \ conf_general.c \ timeout.c \ sleep.c \ - comm_uart.c + comm_uart.c \ + selftest.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. diff --git a/bms_if.c b/bms_if.c index 55dff236..e4f47b94 100644 --- a/bms_if.c +++ b/bms_if.c @@ -201,19 +201,16 @@ static THD_FUNCTION(balance_thd, p) { bool is_balance_override = false; int bal_ch = 0; - for (int i = backup.config.cell_first_index;i < - (backup.config.cell_num + backup.config.cell_first_index);i++) { - for (int i = backup.config.cell_first_index;i < - (backup.config.cell_num + backup.config.cell_first_index);i++) { - if (m_balance_override[i] == 1) { - is_balance_override = true; - ltc_set_dsc(i, false); - } else if (m_balance_override[i] == 2) { - is_balance_override = true; - ltc_set_dsc(i, true); - bal_ch++; - m_is_balancing = true; - } + for (int i = backup.config.cell_first_index; + i < (backup.config.cell_num + backup.config.cell_first_index);i++) { + if (m_balance_override[i] == 1) { + is_balance_override = true; + ltc_set_dsc(i, false); + } else if (m_balance_override[i] == 2) { + is_balance_override = true; + ltc_set_dsc(i, true); + bal_ch++; + m_is_balancing = true; } } @@ -468,6 +465,18 @@ bool bms_if_is_balancing(void) { return m_is_balancing; } +/** + * Override balancing. + * + * cell + * Cell number + * + * override + * Override setting. + * 0: Do not override balancing + * 1: Override and disable balancing on cell + * 2: Override and enable balancing on cell + */ void bms_if_set_balance_override(int cell, int override) { if (cell >= 0 && cell < HW_CELLS_SERIES) { m_balance_override[cell] = override; diff --git a/drivers/ltc6813.c b/drivers/ltc6813.c index f26c0a25..7480018e 100644 --- a/drivers/ltc6813.c +++ b/drivers/ltc6813.c @@ -33,10 +33,12 @@ static THD_WORKING_AREA(ltc_thd_wa, 2048); static volatile float m_v_pack = 0.0; static volatile float m_v_cell[18] = {0.0}; +static volatile float m_v_cell_no_mute[18] = {0.0}; static volatile float m_v_cell_pu_diff[18] = {0.0}; static volatile float m_v_gpio[9] = {-1.0}; static volatile float m_last_temp = 0.0; static volatile bool m_discharge_state[18] = {false}; +static mutex_t ltc_mtx; // Private functions static THD_FUNCTION(ltc_thd, arg); @@ -60,6 +62,8 @@ void ltc_init(void) { palSetLineMode(LINE_LTC_MOSI, PAL_MODE_OUTPUT_PUSHPULL); palSetLine(LINE_LTC_MOSI); + chMtxObjectInit(<c_mtx); + chThdCreateStatic(ltc_thd_wa, sizeof(ltc_thd_wa), NORMALPRIO - 1, ltc_thd, 0); (void)ltc_wakeup(); @@ -81,6 +85,14 @@ float ltc_last_cell_voltage(int cell) { return m_v_cell[cell]; } +float ltc_last_cell_voltage_no_mute(int cell) { + if (cell < 0 || cell > 17) { + return -1.0; + } + + return m_v_cell_no_mute[cell]; +} + float ltc_last_pu_diff_voltage(int cell) { if (cell < 0 || cell > 17) { return -1.0; @@ -120,11 +132,35 @@ void ltc_sleep(void) { write_reg_group(LTC_WRCFGA, buffer); } +bool ltc_self_test(void) { + bool res = true; + + chMtxLock(<c_mtx); + + float cells_st[18]; + write_cmd(LTC_CVST | LTC_MD10 | LTC_ST01); + poll_adc(); + read_cell_voltages((float*)cells_st); + + for (int i = 0;i < 18;i++) { + if ((uint16_t)(cells_st[i] * 1e4) != 0x9555) { + res = false; + break; + } + } + + chMtxUnlock(<c_mtx); + + return res; +} + static THD_FUNCTION(ltc_thd, p) { (void)p; chRegSetThreadName("LTC"); while (!chThdShouldTerminateX()) { + chMtxLock(<c_mtx); + uint8_t buffer[100]; // ltc_wakeup(); @@ -155,24 +191,28 @@ static THD_FUNCTION(ltc_thd, p) { SET_BIT(buffer[1], 0, m_discharge_state[16]); write_reg_group(LTC_WRCFGB, buffer); + write_cmd(LTC_ADCV | LTC_MD10 | LTC_DCP); + poll_adc(); + read_cell_voltages((float*)m_v_cell_no_mute); + write_cmd(LTC_MUTE); - chThdSleepMilliseconds(1); + chThdSleepMilliseconds(10); write_cmd(LTC_ADCV | LTC_MD10); poll_adc(); read_cell_voltages((float*)m_v_cell); // Open wire check - float cells_pu[18], cells_pd[18]; - write_cmd(LTC_ADOW | LTC_MD10); - poll_adc(); - read_cell_voltages(cells_pu); - write_cmd(LTC_ADOW | LTC_MD10 | LTC_PUP); - poll_adc(); - read_cell_voltages(cells_pd); - for (int i = 0;i < 18;i++) { - m_v_cell_pu_diff[i] = cells_pu[i] - cells_pd[i]; - } +// float cells_pu[18], cells_pd[18]; +// write_cmd(LTC_ADOW | LTC_MD10 | LTC_PUP); +// poll_adc(); +// read_cell_voltages(cells_pd); +// write_cmd(LTC_ADOW | LTC_MD10); +// poll_adc(); +// read_cell_voltages(cells_pu); +// for (int i = 0;i < 18;i++) { +// m_v_cell_pu_diff[i] = cells_pu[i] - cells_pd[i]; +// } write_cmd(LTC_ADSTAT | LTC_MD10); poll_adc(); @@ -198,7 +238,9 @@ static THD_FUNCTION(ltc_thd, p) { m_v_gpio[4] = (float)((uint16_t)buffer[2] | (uint16_t)buffer[3] << 8) / 1e4; } - chThdSleepMilliseconds(100); + chMtxUnlock(<c_mtx); + + chThdSleepMilliseconds(90); } } diff --git a/drivers/ltc6813.h b/drivers/ltc6813.h index d645565d..86e1fe00 100644 --- a/drivers/ltc6813.h +++ b/drivers/ltc6813.h @@ -27,15 +27,18 @@ void ltc_init(void); float ltc_last_pack_voltage(void); float ltc_last_temp(void); float ltc_last_cell_voltage(int cell); +float ltc_last_cell_voltage_no_mute(int cell); float ltc_last_pu_diff_voltage(int cell); float ltc_last_gpio_voltage(int gpio); void ltc_set_dsc(int cell, bool set); bool ltc_get_dsc(int cell); void ltc_sleep(void); +bool ltc_self_test(void); // Commands #define LTC_ADCV 0x0260 #define LTC_ADOW 0x0228 +#define LTC_CVST 0x0207 #define LTC_ADSTAT 0x0468 #define LTC_ADCVSC 0x0467 #define LTC_ADAX 0x0460 @@ -70,6 +73,11 @@ void ltc_sleep(void); #define LTC_CHG000 0x0000 // GPIO 1-5, 2nd ref, GPIO 6-9 #define LTC_CHG101 0x0005 // GPIO 5 #define LTC_PUP 0x0040 +#define LTC_ST00 0x0000 +#define LTC_ST01 0x0020 +#define LTC_ST10 0x0040 +#define LTC_ST11 0x0060 + // Configuration bits #define LTC_REFON 0x04 diff --git a/hwconf/hw_12s7p_v1.h b/hwconf/hw_12s7p_v1.h index 508c4c41..72f426f3 100644 --- a/hwconf/hw_12s7p_v1.h +++ b/hwconf/hw_12s7p_v1.h @@ -22,6 +22,8 @@ #define HW_NAME "12s7p" +#define HW_NO_CH0_TEST + // HW-specific #define HW_INIT_HOOK() palSetLineMode(LINE_CURR_MEASURE_EN, PAL_MODE_OUTPUT_PUSHPULL) diff --git a/hwconf/hw_18s_light.h b/hwconf/hw_18s_light.h index 5a036615..67655c67 100644 --- a/hwconf/hw_18s_light.h +++ b/hwconf/hw_18s_light.h @@ -36,6 +36,8 @@ #define HW_NAME "18s_light" +#define HW_NO_CH0_TEST + // HW-specific #define HW_INIT_HOOK() palSetLineMode(LINE_CAN_EN, PAL_MODE_OUTPUT_PUSHPULL); \ palSetLineMode(LINE_CURR_MEASURE_EN, PAL_MODE_OUTPUT_PUSHPULL) diff --git a/hwconf/hw_stormcore_bms.h b/hwconf/hw_stormcore_bms.h index 1566a25f..95413b9a 100644 --- a/hwconf/hw_stormcore_bms.h +++ b/hwconf/hw_stormcore_bms.h @@ -22,6 +22,8 @@ #define HW_NAME "stormcore_bms" +#define HW_NO_CH0_TEST + // HW-specific #define HW_INIT_HOOK() palSetLineMode(LINE_CURR_MEASURE_EN, PAL_MODE_OUTPUT_PUSHPULL) diff --git a/main.c b/main.c index ed68c9b7..9222feb2 100644 --- a/main.c +++ b/main.c @@ -34,6 +34,7 @@ #include "flash_helper.h" #include "comm_uart.h" #include "hw.h" +#include "selftest.h" #include #include @@ -191,6 +192,7 @@ int main(void) { sleep_init(); timeout_init(); + selftest_init(); for(;;) { backup.controller_id = backup.config.controller_id; diff --git a/selftest.c b/selftest.c new file mode 100644 index 00000000..83707a92 --- /dev/null +++ b/selftest.c @@ -0,0 +1,90 @@ +/* + Copyright 2021 Benjamin Vedder benjamin@vedder.se + + This file is part of the VESC BMS firmware. + + The VESC BMS firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The VESC BMS firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "selftest.h" +#include "terminal.h" +#include "commands.h" +#include "ltc6813.h" +#include "bms_if.h" +#include "main.h" +#include "ch.h" +#include "hal.h" + +// Private functions +static void terminal_st(int argc, const char **argv); + +void selftest_init(void) { + terminal_register_command_callback( + "hw_st", + "Run hardware self test", + 0, + terminal_st); +} + +static void terminal_st(int argc, const char **argv) { + (void)argc; (void)argv; + + bool res_ok = ltc_self_test(); + + commands_printf("Testing LTC6813... %s\n", res_ok ? "Ok" : "Failed"); + + for (int i = 0;i < backup.config.cell_num;i++) { + bms_if_set_balance_override(i, 1); + } + + chThdSleepMilliseconds(500); + + commands_printf("Testing balancing and open connections...\n"); + commands_printf("Cell NoBal Bal Diff Result"); + commands_printf("==================================="); + + for (int i = backup.config.cell_first_index; + i < (backup.config.cell_first_index + backup.config.cell_num);i++) { + bms_if_set_balance_override(i, 2); + ltc_set_dsc(i, 1); + chThdSleepMilliseconds(200); + + float no_bal = ltc_last_cell_voltage(i); + float bal = ltc_last_cell_voltage_no_mute(i); + float diff = fabsf(bal - no_bal); + bool ok = (diff / no_bal > 0.03 && bal > 2.0); + +#ifdef HW_NO_CH0_TEST + if (i == 0) { + ok = bal > 2.0; + } +#endif + + commands_printf("%02d %.4f %.4f %.4f %s", + i + 1, + no_bal, + bal, + diff, + ok ? "Ok" : "Failed"); + + if (!ok) { + res_ok = false; + } + + bms_if_set_balance_override(i, 0); + ltc_set_dsc(i, 0); + } + + commands_printf(res_ok ? "\nAll tests passed!\n" : "\nOne or more tests failed...\n"); +} diff --git a/selftest.h b/selftest.h new file mode 100644 index 00000000..e54e4b6a --- /dev/null +++ b/selftest.h @@ -0,0 +1,26 @@ +/* + Copyright 2021 Benjamin Vedder benjamin@vedder.se + + This file is part of the VESC BMS firmware. + + The VESC BMS firmware is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The VESC BMS firmware is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifndef SELFTEST_H_ +#define SELFTEST_H_ + +// Functions +void selftest_init(void); + +#endif /* SELFTEST_H_ */