From 6422dcea9c87d417d0b69ba497f3488a7f807219 Mon Sep 17 00:00:00 2001 From: Dragon-Knight Date: Fri, 27 Dec 2024 03:21:38 +0300 Subject: [PATCH] Use OneWire library & cleanup --- include/BMSLogic.h | 74 ++++- include/CANLogic.h | 12 +- include/OneWire.h | 38 +++ lib/PixelBMSLibrary/src/bms_ant.h | 121 -------- lib/PixelBMSLibrary/src/bms_ant_data.h | 170 ---------- lib/can_classes/BMS_low_level_abstraction.cpp | 51 --- lib/can_classes/BMS_low_level_abstraction.h | 87 ------ platformio.ini | 1 + src/ds18b20.cpp | 293 ------------------ src/ds18b20.h | 44 --- src/main.cpp | 177 ++++------- src/stm32f1xx_hal_conf.h | 2 +- 12 files changed, 171 insertions(+), 899 deletions(-) create mode 100644 include/OneWire.h delete mode 100644 lib/PixelBMSLibrary/src/bms_ant.h delete mode 100644 lib/PixelBMSLibrary/src/bms_ant_data.h delete mode 100644 lib/can_classes/BMS_low_level_abstraction.cpp delete mode 100644 lib/can_classes/BMS_low_level_abstraction.h delete mode 100644 src/ds18b20.cpp delete mode 100644 src/ds18b20.h diff --git a/include/BMSLogic.h b/include/BMSLogic.h index 51ee38c..8299818 100644 --- a/include/BMSLogic.h +++ b/include/BMSLogic.h @@ -1,9 +1,4 @@ #pragma once - -#include "BMS_low_level_abstraction.h" - -#include - #include #include #include @@ -48,6 +43,73 @@ namespace BMSLogic { DEBUG_LOG_TOPIC("BMS-ERR", "idx: %d, code: %d\n", idx, code); } + + + void UpdateCANObjects_BMS1(const BMSANT::packet_raw_reverse_t *data) + { + timer_type_t timer_type = CAN_TIMER_TYPE_NORMAL; + event_type_t event_type = CAN_EVENT_TYPE_NONE; + + + + CANLib::obj_high_current_1.SetValue(0, data->total_current, CAN_TIMER_TYPE_NORMAL); + + + timer_type = CAN_TIMER_TYPE_NORMAL; + if (data->capacity_percent < 15) + { + timer_type = CAN_TIMER_TYPE_CRITICAL; + } + else if (data->capacity_percent < 30) + { + timer_type = CAN_TIMER_TYPE_WARNING; + } + CANLib::obj_battery_percent_1.SetValue(0, data->capacity_percent, timer_type); + + + + CANLib::obj_battery_power_1.SetValue(0, data->total_power, CAN_TIMER_TYPE_NORMAL); + //CANLib::obj_battery_state_1.SetValue(0, data->, CAN_TIMER_TYPE_NORMAL); + CANLib::obj_high_voltage_1.SetValue(0, data->total_voltage, CAN_TIMER_TYPE_NORMAL); + + + + timer_type = CAN_TIMER_TYPE_NORMAL; + event_type = CAN_EVENT_TYPE_NONE; + if(data->cell_vmin_volt >= 2700 && data->cell_vmin_volt <= 3000) + { + timer_type = CAN_TIMER_TYPE_WARNING; + } + else if(data->cell_vmin_volt < 2700 || data->cell_vmax_volt > 4200) + { + timer_type = CAN_TIMER_TYPE_CRITICAL; + event_type = CAN_EVENT_TYPE_ERROR; + } + CANLib::obj_low_voltage_min_max_delta_1.SetValue(0, data->cell_vmin_volt, timer_type, event_type); + CANLib::obj_low_voltage_min_max_delta_1.SetValue(1, data->cell_vmax_volt, timer_type, event_type); + CANLib::obj_low_voltage_min_max_delta_1.SetValue(2, (data->cell_vmax_volt - data->cell_vmin_volt), timer_type, event_type); + + + + //CANLib::obj_low_voltage_batt_1.SetValue(0, data->total_voltage, CAN_TIMER_TYPE_NORMAL); + //CANLib::obj_max_temperature_1.SetValue(0, data->total_voltage, CAN_TIMER_TYPE_NORMAL); + //CANLib::obj_temperature_1.SetValue(0, data->total_voltage, CAN_TIMER_TYPE_NORMAL); + + + + + + + + + + + + + + + //UpdateMaxTemperature(); + } inline void Setup() @@ -57,7 +119,7 @@ namespace BMSLogic uart_data[BMS_1].hal = &hBms1Uart; uart_data[BMS_2].hal = &hBms2Uart; - Ant1.SetReadyCallback( CANLib::UpdateCANObjects_BMS ); + Ant1.SetReadyCallback( UpdateCANObjects_BMS1 ); //Ant2.SetReadyCallback(); Bms.SetModel(BMS_1, Ant1); diff --git a/include/CANLogic.h b/include/CANLogic.h index c1ed25e..3304b05 100644 --- a/include/CANLogic.h +++ b/include/CANLogic.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include "BMS_low_level_abstraction.h" #include extern CAN_HandleTypeDef hcan; @@ -347,7 +346,7 @@ namespace CANLib } for (uint8_t i = 0; i < 7; i++) { - curr_temp = obj_temperature_3.GetValue(i); + //curr_temp = obj_temperature_3.GetValue(i); if (curr_temp > max_temp) max_temp = curr_temp; } @@ -365,10 +364,11 @@ namespace CANLib } // TODO: надо ещё обсудить по event_type и его присваивать - obj_max_temperature.SetValue(0, max_temp, timer_type, event_type); + //obj_max_temperature.SetValue(0, max_temp, timer_type, event_type); } - void UpdateCANObjects_BMS(const BMSANT::packet_raw_reverse_t *data) +/* + void UpdateCANObjects_BMS1(const BMSANT::packet_raw_reverse_t *data) { //packet_structure_t *bms_packet_struct = (packet_structure_t *)bms_raw_packet_data; @@ -578,7 +578,7 @@ namespace CANLib // Максимально зафиксированная температура. UpdateMaxTemperature(); } - +*/ void UpdateCANObjects_ExternalTemperature(int8_t *temperature_data, uint8_t data_count) { if (temperature_data == nullptr) @@ -618,7 +618,7 @@ namespace CANLib { if (data_index >= data_count) return; - obj_temperature_3.SetValue(i, temperature_data[data_index++]); + //obj_temperature_3.SetValue(i, temperature_data[data_index++]); } // 0x0046 MaxTemperature diff --git a/include/OneWire.h b/include/OneWire.h new file mode 100644 index 0000000..4081728 --- /dev/null +++ b/include/OneWire.h @@ -0,0 +1,38 @@ +#pragma once +#include + +extern TIM_HandleTypeDef htim1; + +namespace OneWire +{ + OneWireDriver oneWire(GPIOB, GPIO_PIN_9, &htim1); + OneWireTSensEx<16> sensors(oneWire); + + + inline void Setup() + { + sensors.RegReadyCallback([](OneWireTSensEx<16>::sensor_t *obj, uint8_t count) -> void + { + for(uint8_t i = 0; i < count; ++i) + { + Logger.Printf("Rom: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X, Temp: %05d°C, Valid: %d, Min: %d, Mid: %d, Max: %d", + obj[i].rom->raw[0], obj[i].rom->raw[1], obj[i].rom->raw[2], obj[i].rom->raw[3], + obj[i].rom->raw[4], obj[i].rom->raw[5], obj[i].rom->raw[6], obj[i].rom->raw[7], + obj[i].temp, obj[i].valid, sensors.GetMinTemp(), sensors.GetMidTemp(), sensors.GetMaxTemp()).PrintNewLine(); + } + }); + + return; + } + + inline void Loop(uint32_t ¤t_time) + { + sensors.Processing(current_time); + + + // При выходе обновляем время + current_time = HAL_GetTick(); + + return; + } +}; diff --git a/lib/PixelBMSLibrary/src/bms_ant.h b/lib/PixelBMSLibrary/src/bms_ant.h deleted file mode 100644 index 9cad6a2..0000000 --- a/lib/PixelBMSLibrary/src/bms_ant.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - -*/ - -#pragma once - -#include -#include -#include - -class bms_ant -{ - using callback_tx_t = void (*)(uint8_t *data, uint8_t length); - - public: - bms_ant(callback_tx_t tx_func) : _callback_tx(tx_func) - { - - } - - void Init() - { -// memset(¶ms, 0x00, sizeof(params)); - _params_idx = 0; - _ready = false; - - return; - } - - /* - Пакет получен, распарсен, проверен и готов к выдаче данных. - */ - bool IsReady() - { - return _ready; - } - - - void InputData(uint8_t *data, uint8_t length, uint32_t time) - { - if(length != BMSANTLib::PacketSize) return; - - if(time - _last_inputdata_time > 250) - { - //reset - } - _last_inputdata_time = time; - - memcpy(_buffer, data, length); - for(uint8_t li = 0, ri = 0; li < BMSANTLib::PacketSize / 2; li++) - { - ri = BMSANTLib::PacketSize - li - 1; - uint8_t tmp = _buffer[li]; - _buffer[li] = _buffer[ri]; - _buffer[ri] = tmp; - } - - _CheckBuffer(); - - - - - - - - } - - - - private: - - void _CheckBuffer() - { - BMSANTLib::packet_raw_t *packet = (BMSANTLib::packet_raw_t *) _buffer; - - if(packet->header == *((uint32_t *) BMSANTLib::PacketHeader)) - { - if(packet->crc == _CRCBuffer()) - { - _state = STATE_RECEIVED; - } - else - { - _error = ERROR_CRC; - } - } - else - { - _error = ERROR_FORMAT; - } - - return; - } - - uint16_t _CRCBuffer() - { - uint16_t result = 0x0000; - - for(uint8_t i = 2; i < BMSANTLib::PacketSize - 4; ++i) - { - result += _buffer[i]; - } - - return result; - } - - uint8_t _params_idx; - bool _ready; - - - callback_tx_t _callback_tx; - - uint8_t _buffer[BMSANTLib::PacketSize]; - - uint32_t _last_inputdata_time; - - enum { STATE_IDLE, STATE_RECEIVED, STATE_PARSED } _state; - enum { ERROR_NONE, ERROR_CRC, ERROR_FORMAT } _error; - - -}; diff --git a/lib/PixelBMSLibrary/src/bms_ant_data.h b/lib/PixelBMSLibrary/src/bms_ant_data.h deleted file mode 100644 index 3b718cf..0000000 --- a/lib/PixelBMSLibrary/src/bms_ant_data.h +++ /dev/null @@ -1,170 +0,0 @@ -#pragma once -#include - -namespace BMSANTLib -{ - static constexpr uint8_t PacketSize = 140U; - static constexpr uint8_t PacketHeader[] = {0xAA, 0x55, 0xAA, 0xFF}; - static constexpr uint8_t PacketRequest[] = {0x5A, 0x5A, 0x00, 0x00, 0x00, 0x00}; - static constexpr uint8_t CellsNumber = 32U; - static constexpr uint8_t TempsNumber = 6U; - - enum charge_mosfet_status_t : uint8_t - { - CHARGE_OFF = 0U, - CHARGE_ON = 1U, - CHARGE_CELL_VOLTAGE_HIGH = 2U, - CHARGE_OVER_CURRENT = 3U, - CHARGE_TOTAL_VOLTAGE_HIGH = 5U, - CHARGE_TEMPERATURE_HIGH = 6U, - CHARGE_MOS_TEMPERATURE_HIGH = 7U, - CHARGE_CURRENT_ERROR = 8U, - CHARGE_WIRE_DISCONNECTED_ERROR = 9U, - CHARGE_BOARD_HIGH_TEMPERATURE = 10U, - CHARGE_OPEN_FAILURE = 12U, - CHARGE_CHARGE_MOS_ERROR = 13U, - CHARGE_WAITING = 14U, - CHARGE_MANUAL_OFF = 15U, - CHARGE_LEVEL2_CELL_VOLT_HIGH = 16U, - CHARGE_LOW_TEMPERATURE = 17U, - CHARGE_CELL_VOLT_DIFF = 18U, - CHARGE_CELL_NUMBER_ERROR = 22U - }; - - enum discharge_mosfet_status_t : uint8_t - { - DISCHARGE_OFF = 0U, - DISCHARGE_ON = 1U, - DISCHARGE_CELL_VOLTAGE_LOW = 2U, - DISCHARGE_OVER_CURRENT = 3U, - DISCHARGE_LEVEL2_OVER_CURRENT = 4U, - DISCHARGE_TOTAL_VOLTAGE_LOW = 5U, - DISCHARGE_TEMPERATURE_HIGH = 6U, - DISCHARGE_MOS_TEMPERATURE_HIGH = 7U, - DISCHARGE_CURRENT_ERROR = 8U, - DISCHARGE_WIRE_DISCONNECTED_ERROR = 9U, - DISCHARGE_BOARD_HIGH_TEMPERATURE = 10U, - DISCHARGE_SHORT_CIRCUIT = 12U, - DISCHARGE_MOS_ERROR = 13U, - DISCHARGE_PRECHARGE_FAILURE = 14U, - DISCHARGE_MANUAL_OFF = 15U, - DISCHARGE_LEVEL_2_CELL_VOLT_LOW = 16U, - DISCHARGE_LOW_TEMPERATURE = 17U, - DISCHARGE_CELL_VOLT_DIFF = 18U, - DISCHARGE_CELL_NUMBER_ERROR = 22U - }; - - enum balancer_status_t : uint8_t - { - BALANCER_OFF = 0U, - BALANCER_CELL_VOLT_LIMIT_BALANCE = 1U, - BALANCER_CELL_DIFF_BALANCE = 2U, - BALANCER_BALANCE_TEMP_HIGH = 3U, - BALANCER_AUTO_BALANCE = 4U, - BALANCER_MOS_TEMP_HIGH = 10U - }; - - struct packet_raw_t - { - uint32_t header; // Заголовок пакета. - uint16_t total_voltage; // Напряжение на всей АКБ, 0.1 V - uint16_t cell_voltage_1; // Напряжение на 1 ячейки, 0.001 V - uint16_t cell_voltage_2; // Напряжение на 2 ячейки, 0.001 V - uint16_t cell_voltage_3; // Напряжение на 3 ячейки, 0.001 V - uint16_t cell_voltage_4; // Напряжение на 4 ячейки, 0.001 V - uint16_t cell_voltage_5; // Напряжение на 5 ячейки, 0.001 V - uint16_t cell_voltage_6; // Напряжение на 6 ячейки, 0.001 V - uint16_t cell_voltage_7; // Напряжение на 7 ячейки, 0.001 V - uint16_t cell_voltage_8; // Напряжение на 8 ячейки, 0.001 V - uint16_t cell_voltage_9; // Напряжение на 9 ячейки, 0.001 V - uint16_t cell_voltage_10; // Напряжение на 10 ячейки, 0.001 V - uint16_t cell_voltage_11; // Напряжение на 11 ячейки, 0.001 V - uint16_t cell_voltage_12; // Напряжение на 12 ячейки, 0.001 V - uint16_t cell_voltage_13; // Напряжение на 13 ячейки, 0.001 V - uint16_t cell_voltage_14; // Напряжение на 14 ячейки, 0.001 V - uint16_t cell_voltage_15; // Напряжение на 15 ячейки, 0.001 V - uint16_t cell_voltage_16; // Напряжение на 16 ячейки, 0.001 V - uint16_t cell_voltage_17; // Напряжение на 17 ячейки, 0.001 V - uint16_t cell_voltage_18; // Напряжение на 18 ячейки, 0.001 V - uint16_t cell_voltage_19; // Напряжение на 19 ячейки, 0.001 V - uint16_t cell_voltage_20; // Напряжение на 20 ячейки, 0.001 V - uint16_t cell_voltage_21; // Напряжение на 21 ячейки, 0.001 V - uint16_t cell_voltage_22; // Напряжение на 22 ячейки, 0.001 V - uint16_t cell_voltage_23; // Напряжение на 23 ячейки, 0.001 V - uint16_t cell_voltage_24; // Напряжение на 24 ячейки, 0.001 V - uint16_t cell_voltage_25; // Напряжение на 25 ячейки, 0.001 V - uint16_t cell_voltage_26; // Напряжение на 26 ячейки, 0.001 V - uint16_t cell_voltage_27; // Напряжение на 27 ячейки, 0.001 V - uint16_t cell_voltage_28; // Напряжение на 28 ячейки, 0.001 V - uint16_t cell_voltage_29; // Напряжение на 29 ячейки, 0.001 V - uint16_t cell_voltage_30; // Напряжение на 30 ячейки, 0.001 V - uint16_t cell_voltage_31; // Напряжение на 31 ячейки, 0.001 V - uint16_t cell_voltage_32; // Напряжение на 32 ячейки, 0.001 V - int32_t total_current; // Ток разряда иди заряда, 0.1 A - uint8_t capacity_percent; // Оставшаяся ёмкость АКБ, 1.0 % - uint32_t capacity_phy; // Фактическая ёмкость АКБ, 0.000001 Ah - uint32_t capacity_rem; // Оставшаяся ёмкость АКБ, 0.000001 Ah - uint32_t capacity_cycle; // , 0.001 Ah - uint32_t uptime; // Кол-во времени с питанием, 1.0 s - int16_t temp_mosfet; // Температура датчика mosfet, 1.0 C - int16_t temp_balans; // Температура датчика balance, 1.0 C - int16_t temp_1; // Температура датчика 1, 1.0 C - int16_t temp_2; // Температура датчика 2, 1.0 C - int16_t temp_3; // Температура датчика 3, 1.0 C - int16_t temp_4; // Температура датчика 4, 1.0 C - uint8_t status_charge_fet; // Флаг состояние ключа зарядки, charge_mosfet_status_t - uint8_t status_dcharge_fet; // Флаг состояние ключа разрядки, discharge_mosfet_status_t - uint8_t status_balancer; // Флаг состояние балансировки, balancer_status_t - uint16_t tire_length; // WAT? - uint16_t pulses_num; // WAT? - uint8_t relay; // WAT? - uint32_t total_power; // Текущая мощность, 1.0 W - uint8_t cell_vmax_num; // Номер батарее с максимальный напряжением. - uint16_t cell_vmax_volt; // Напряжение батареи с максимальным напряжением, 0.001 V - uint8_t cell_vmin_num; // Номер батарее с минимальным напряжением. - uint16_t cell_vmin_volt; // Напряжение батареи с минимальным напряжением, 0.001 V - uint16_t cell_vmid_volt; // Среднее напряжение батареи, 0.001 V - uint8_t cell_count_num; // Кол-во ячеек в АКБ. - uint16_t dcharge_fet_lost; // Падение на транзисторе нагрузки, В. - uint16_t dcharge_fet_v; // Напряжение на затворе транзистора нагрузки, В. - uint16_t charge_fet_v; // Напряжение на затворе транзистора зарядки, В. - uint16_t wat_130131; // WAT? - uint32_t balance_bits; // Флаги сбалансированных ячеек. - uint16_t logs; // WAT? - uint16_t crc; // Контрольная сумма. - }; - - struct packet_raw_reverse_t - { - uint16_t crc; // Контрольная сумма. - uint16_t logs; // WAT? - uint32_t balance_bits; // Флаги сбалансированных ячеек. - uint16_t wat_130131; // WAT? - uint16_t charge_fet_v; // Напряжение на затворе транзистора зарядки, В. - uint16_t dcharge_fet_v; // Напряжение на затворе транзистора нагрузки, В. - uint16_t dcharge_fet_lost; // Падение на транзисторе нагрузки, В. - uint8_t cell_count_num; // Кол-во ячеек в АКБ. - uint16_t cell_vmid_volt; // Среднее напряжение батареи, 0.001 V - uint16_t cell_vmin_volt; // Напряжение батареи с минимальным напряжением, 0.001 V - uint8_t cell_vmin_num; // Номер батарее с минимальным напряжением. - uint16_t cell_vmax_volt; // Напряжение батареи с максимальным напряжением, 0.001 V - uint8_t cell_vmax_num; // Номер батарее с максимальный напряжением. - uint32_t total_power; // Текущая мощность, 1.0 W - uint8_t relay; // WAT? - uint16_t pulses_num; // WAT? - uint16_t tire_length; // WAT? - balancer_status_t status_balancer; // Флаг состояние балансировки, balancer_status_t - discharge_mosfet_status_t status_dcharge_fet; // Флаг состояние ключа разрядки, discharge_mosfet_status_t - charge_mosfet_status_t status_charge_fet; // Флаг состояние ключа зарядки, charge_mosfet_status_t - int16_t temperature[TempsNumber]; // Температура датчиков 1..6, 1.0 C - uint32_t uptime; // Кол-во времени с питанием, 1.0 s - uint32_t capacity_cycle; // , 0.001 Ah - uint32_t capacity_rem; // Оставшаяся ёмкость АКБ, 0.000001 Ah - uint32_t capacity_phy; // Фактическая ёмкость АКБ, 0.000001 Ah - uint8_t capacity_percent; // Оставшаяся ёмкость АКБ, 1.0 % - int32_t total_current; // Ток разряда иди заряда, 0.1 A - uint16_t cell_voltage[CellsNumber]; // Напряжение на ячейках 1..32, 0.001 V - uint16_t total_voltage; // Напряжение на всей АКБ, 0.1 V - uint32_t header; // Заголовок пакета. - }; -} diff --git a/lib/can_classes/BMS_low_level_abstraction.cpp b/lib/can_classes/BMS_low_level_abstraction.cpp deleted file mode 100644 index bdcc478..0000000 --- a/lib/can_classes/BMS_low_level_abstraction.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "BMS_low_level_abstraction.h" - -/// @brief Calculates BMS raw packet data CRC -/// @param bms_packet_data Array with raw BMS data -/// @return Calculated CRC of the BMS packet -uint16_t bms_raw_data_crc(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]) -{ - uint16_t result = 0x0000; - - for (uint8_t i = sizeof(BMS_PACKET_HEADER); i < BMS_BOARD_PACKET_SIZE - 2; ++i) - { - result += *(bms_packet_data + i); - } - - return result; -} - -/// @brief Return CRC value of BMS raw packet -/// @param bms_packet_data Array with raw BMS data -/// @return CRC from the BMS packet -uint16_t get_bms_raw_data_crc(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]) -{ - return *(uint16_t *)&bms_packet_data[BMS_BOARD_PACKET_SIZE - 2]; -} - -/// @brief BMS raw packet data CRC validation -/// @param bms_packet_data Array with raw BMS data -/// @return 'true' if the calculated CRC matches the one stored in the BMS packet -bool bms_raw_data_validation(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]) -{ - if (bms_packet_data == nullptr) - return false; - - return get_bms_raw_data_crc(bms_packet_data) == bms_raw_data_crc(bms_packet_data); -} - -/// @brief Calculates the maximum temperature based on the values from the internal and external BMS temperature sensors. -/// @param temperatures Array of temperatures -/// @return Maximum temperature -int8_t calculate_max_temperature(int8_t temperatures[TOTAL_TEMPERATURE_SENSORS_COUNT]) -{ - int8_t result = INT8_MIN; - - for (uint8_t i = 0; i < TOTAL_TEMPERATURE_SENSORS_COUNT; i++) - { - if (result < temperatures[i]) - result = temperatures[i]; - } - - return result; -} diff --git a/lib/can_classes/BMS_low_level_abstraction.h b/lib/can_classes/BMS_low_level_abstraction.h deleted file mode 100644 index a2367f7..0000000 --- a/lib/can_classes/BMS_low_level_abstraction.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef BMS_DATA_H -#define BMS_DATA_H - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -#define BMS_BOARD_PACKET_SIZE 140 // The size of uart packet of the BMS. Used for buffer declaration -#define BMS_BATTERY_NUMBER_OF_CELLS 32 // Number of cells in BMS packet -#define BMS_PACKET_HEADER 0xAA55AAFF // from SlaveECU github - -// *************************************************************************************************** -// BMS Packet structure -#pragma pack(push, 1) - struct packet_structure_t - { - uint32_t header; // Заголовок пакета. - - uint16_t voltage; // Напряжение на всей АКБ, x10 мВ. - uint16_t cells_voltage[BMS_BATTERY_NUMBER_OF_CELLS]; // Напряжение на каждой из 32-х ячеек АКБ, мВ. - //uint16_t reserved_70; // - int32_t current; // Ток разряда или заряда, мА. - - uint8_t percent; // Оставшаяся ёмкость АКБ, проценты. - uint32_t phy_capacity; // Фактическая ёмкость АКБ, мА/ч. - uint32_t rem_capacity; // Оставшаяся ёмкость АКБ, мА/ч. - uint32_t cycle_capacity; // WAT? - - uint32_t uptime; // Кол-во времени с питанием, секунд. - - // Attention! In case of order changing of the temperature fields we need: - // - edit _bms_packet_temperature_sensor enum, - // - change the get_temperature_by_id() method. - // If temperature fields are not arranged in a row, the get_temperature_by_id() method will be broken. - int16_t temperature_mosfet; // Температура датчика MOSFET, Градусы. - int16_t temperature_balancer; // Температура датчика Balance, Градусы. - int16_t temperature_sensors[4]; // Температура датчиков 1, Градусы. - - uint8_t charge_fet; // Флаг состояние ключа зарядки. - uint8_t dcharge_fet; // Флаг состояние ключа разрядки. - uint8_t balanced; // Флаг состояние балансировки. - uint16_t tire_length; // WAT? - uint16_t pulses_num; // WAT? - uint8_t relay; // WAT? - int32_t power; // Текущая мощность, Вт. - - uint8_t vmax_cell; // Номер ячейки батареи с максимальным напряжением. - uint16_t vmax_voltage; // Напряжение ячейки батареи с максимальным напряжением, мВ. - uint8_t vmin_cell; // Номер ячейки батареи с минимальным напряжением. - uint16_t vmin_voltage; // Напряжение ячейки батареи с минимальным напряжением, мВ. - uint16_t vmid_voltage; // Среднее напряжение батареи, мВ. - - uint8_t unknown_1; // WAT? - uint16_t dcharge_fet_lost; // Падение на транзисторе нагрузки, В. - uint16_t dcharge_fet_v; // Напряжение на затворе транзистора нагрузки, В. - uint16_t charge_fet_v; // Напряжение на затворе транзистора зарядки, В. - uint16_t unknown_2; // WAT? - uint32_t balance_bits; // Флаги сбалансированных ячеек. - uint16_t logs; // WAT? - - uint16_t crc; // Контрольная сумма. - }; -#pragma pack(pop) - static_assert(sizeof(packet_structure_t) == BMS_BOARD_PACKET_SIZE, "BMS_BOARD_PACKET_SIZE const differs with actual size of structure."); - -#define BMS_TEMPERATURE_SENSORS_COUNT 6 -#define EXTERNAL_TEMPERATURE_SENSORS_COUNT 15 -#define TOTAL_TEMPERATURE_SENSORS_COUNT BMS_TEMPERATURE_SENSORS_COUNT + EXTERNAL_TEMPERATURE_SENSORS_COUNT - - // *************************************************************************************************** - // BMS related functions - uint16_t bms_raw_data_crc(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]); - uint16_t get_bms_raw_data_crc(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]); - bool bms_raw_data_validation(uint8_t bms_packet_data[BMS_BOARD_PACKET_SIZE]); - int8_t calculate_max_temperature(int8_t temperatures[TOTAL_TEMPERATURE_SENSORS_COUNT]); - -#ifdef __cplusplus -} -#endif - -#endif // BMS_DATA_H \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 1dc2541..b840837 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,6 +22,7 @@ lib_deps = https://github.com/starfactorypixel/Library_PixelEasyPins https://github.com/starfactorypixel/Library_PixelSPI https://github.com/starfactorypixel/Library_PixelAnalogMux + https://github.com/starfactorypixel/Library_PixelOneWire debug_tool = stlink monitor_speed = 500000 monitor_port = COM10 diff --git a/src/ds18b20.cpp b/src/ds18b20.cpp deleted file mode 100644 index 2f55f44..0000000 --- a/src/ds18b20.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "ds18b20.h" - -//-------------------------------------------------- -uint8_t LastDeviceFlag; -uint8_t LastDiscrepancy; -uint8_t LastFamilyDiscrepancy; -uint8_t ROM_NO[8]; -extern uint8_t Dev_ID[8][8]; -extern uint8_t Dev_Cnt; -//-------------------------------------------------- -__STATIC_INLINE void DelayMicro(__IO uint32_t micros) -{ -micros *= (SystemCoreClock / 1025000) / 8; // micros *= (SystemCoreClock / 1000000) / 9; -/* Wait till done */ -while (micros--) ; -} - -void DelayMicr(uint32_t cn) -{ - DelayMicro(cn); -} -//-------------------------------------------------- -void port_init(void) -{ - HAL_GPIO_DeInit(DS_PORT, GPIO_DS_PIN); - DS_PORT->CRH |= GPIO_CRH_MODE9; - DS_PORT->CRH |= GPIO_CRH_CNF9_0; - DS_PORT->CRH &= ~GPIO_CRH_CNF9_1; -} - -//********************************************************************************************* -//function импульс сброса // -//argument маска порта // -//return 0 - устройство обнаружен, 1 - не обнаружено, 2 - к.з. на линии // -//********************************************************************************************* -uint8_t ds_reset_pulse(uint16_t PinMask) -{ - uint16_t result; - - if((DS_PORT->IDR & PinMask)==0) return 2; //проверить линию на отсутствие замыкания - DS_PORT->ODR &= ~PinMask; //потянуть шину к земле - DelayMicro(485); //задержка как минимум на 480 микросекунд - DS_PORT->ODR |= PinMask; //отпустить шину - DelayMicro(65); //задержка как минимум на 65 микросекунд - result = DS_PORT->IDR & PinMask; //прочитать шину - DelayMicro(500); //задержка как минимум на 480 микросекунд - if(result) return 1; //датчик не обнаружен - return 0; //датчик обнаружен -} - -//-------------------------------------------------- -uint8_t ds18b20_Reset(void) -{ - uint16_t status; - DS_PORT->ODR &= ~GPIO_DS_PIN_OUT;//низкий уровень - DelayMicro(485);//задержка как минимум на 480 микросекунд - DS_PORT->ODR |= GPIO_DS_PIN_OUT;//высокий уровень - DelayMicro(65);//задержка как минимум на 60 микросекунд - status = DS_PORT->IDR & GPIO_DS_PIN_READ;//проверяем уровень - DelayMicro(500);//задержка как минимум на 480 микросекунд - //(на всякий случай подождём побольше, так как могут быть неточности в задержке) - return (status ? 1 : 0);//вернём результат -} -//---------------------------------------------------------- -uint8_t ds18b20_ReadBit(void) -{ - uint8_t bit = 0; - DS_PORT->ODR &= ~GPIO_DS_PIN_OUT;//низкий уровень - DelayMicro(2); - DS_PORT->ODR |= GPIO_DS_PIN_OUT;//высокий уровень - DelayMicro(13); - bit = (DS_PORT->IDR & GPIO_DS_PIN_READ ? 1 : 0);//проверяем уровень - DelayMicro(45); - return bit; -} -//----------------------------------------------- -uint8_t ds18b20_ReadByte(void) -{ - uint8_t data = 0; - for (uint8_t i = 0; i <= 7; i++) - data += ds18b20_ReadBit() << i; - return data; -} -//----------------------------------------------- -void ds18b20_WriteBit(uint8_t bit) -{ - DS_PORT->ODR &= ~GPIO_DS_PIN_OUT; - DelayMicro(bit ? 3 : 65); - DS_PORT->ODR |= GPIO_DS_PIN_OUT; - DelayMicro(bit ? 65 : 3); -} -//----------------------------------------------- -void ds18b20_WriteByte(uint8_t dt) -{ - for (uint8_t i = 0; i < 8; i++) - { - ds18b20_WriteBit(dt >> i & 1); - //Delay Protection - DelayMicro(5); - } -} -//----------------------------------------------- -uint8_t ds18b20_SearhRom(uint8_t *Addr) -{ - uint8_t id_bit_number; - uint8_t last_zero, rom_byte_number, search_result; - uint8_t id_bit, cmp_id_bit; - uint8_t rom_byte_mask, search_direction; - //проинициализируем переменные - id_bit_number = 1; - last_zero = 0; - rom_byte_number = 0; - rom_byte_mask = 1; - search_result = 0; - if (!LastDeviceFlag) - { - ds18b20_Reset(); - ds18b20_WriteByte(0xF0); - } - do - { - id_bit = ds18b20_ReadBit(); - cmp_id_bit = ds18b20_ReadBit(); - if ((id_bit == 1) && (cmp_id_bit == 1)) - break; - else - { - if (id_bit != cmp_id_bit) - search_direction = id_bit; // bit write value for search - else - { - if (id_bit_number < LastDiscrepancy) - search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); - else - search_direction = (id_bit_number == LastDiscrepancy); - if (search_direction == 0) - { - last_zero = id_bit_number; - if (last_zero < 9) - LastFamilyDiscrepancy = last_zero; - } - } - if (search_direction == 1) - ROM_NO[rom_byte_number] |= rom_byte_mask; - else - ROM_NO[rom_byte_number] &= ~rom_byte_mask; - ds18b20_WriteBit(search_direction); - id_bit_number++; - rom_byte_mask <<= 1; - if (rom_byte_mask == 0) - { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } while(rom_byte_number < 8); // считываем байты с 0 до 7 в цикле - if (!(id_bit_number < 65)) - { - // search successful so set LastDiscrepancy,LastDeviceFlag,search_result - LastDiscrepancy = last_zero; - // check for last device - if (LastDiscrepancy == 0) - LastDeviceFlag = 1; - search_result = 1; - } - if (!search_result || !ROM_NO[0]) - { - LastDiscrepancy = 0; - LastDeviceFlag = 0; - LastFamilyDiscrepancy = 0; - search_result = 0; - } - else - { - for (int i = 0; i < 8; i++) Addr[i] = ROM_NO[i]; - } - return search_result; -} -//----------------------------------------------- -uint8_t ds18b20_init(uint8_t mode) -{ - int i = 0, j=0; - uint8_t dt[8]; - if(mode==SKIP_ROM) - { - if(ds18b20_Reset()) return 1; - //SKIP ROM - ds18b20_WriteByte(0xCC); - //WRITE SCRATCHPAD - ds18b20_WriteByte(0x4E); - //TH REGISTER 100 градусов - ds18b20_WriteByte(0x64); - //TL REGISTER - 30 градусов - ds18b20_WriteByte(0x9E); - //Resolution 12 bit - ds18b20_WriteByte(RESOLUTION_12BIT); - } - else - { - for(i=1;i<=8;i++) - { - if(ds18b20_SearhRom(dt)) - { - Dev_Cnt++; - memcpy(Dev_ID[Dev_Cnt-1],dt,sizeof(dt)); - } - else break; - } - for(i=1;i<=Dev_Cnt;i++) - { - if(ds18b20_Reset()) return 1; - //Match Rom - ds18b20_WriteByte(0x55); - for(j=0;j<=7;j++) - { - ds18b20_WriteByte(Dev_ID[i-1][j]); - } - //WRITE SCRATCHPAD - ds18b20_WriteByte(0x4E); - //TH REGISTER 100 градусов - ds18b20_WriteByte(0x64); - //TL REGISTER - 30 градусов - ds18b20_WriteByte(0x9E); - //Resolution 12 bit - ds18b20_WriteByte(RESOLUTION_12BIT); - } - } - return 0; -} -//---------------------------------------------------------- -void ds18b20_MeasureTemperCmd(uint8_t mode, uint8_t DevNum) -{ - int i = 0; - ds18b20_Reset(); - if(mode==SKIP_ROM) - { - //SKIP ROM - ds18b20_WriteByte(0xCC); - } - else - { - //Match Rom - ds18b20_WriteByte(0x55); - for(i=0;i<=7;i++) - { - ds18b20_WriteByte(Dev_ID[DevNum-1][i]); - } - } - //CONVERT T - ds18b20_WriteByte(0x44); -} -//---------------------------------------------------------- -void ds18b20_ReadStratcpad(uint8_t mode, uint8_t *Data, uint8_t DevNum) -{ - uint8_t i; - ds18b20_Reset(); - if(mode==SKIP_ROM) - { - //SKIP ROM - ds18b20_WriteByte(0xCC); - } - else - { - //Match Rom - ds18b20_WriteByte(0x55); - for(i=0;i<=7;i++) - { - ds18b20_WriteByte(Dev_ID[DevNum-1][i]); - } - } - //READ SCRATCHPAD - ds18b20_WriteByte(0xBE); - for(i=0;i<8;i++) - { - Data[i] = ds18b20_ReadByte(); - } -} -//---------------------------------------------------------- -bool ds18b20_GetSign(uint16_t dt) -{ - //Проверим 11-й бит - return (dt&(1<<11)); -} -//---------------------------------------------------------- -float ds18b20_Convert(uint16_t dt) -{ - float t; - t = (float) ((dt&0x07FF)>>4); //отборосим знаковые и дробные биты - //Прибавим дробную часть - t += (float)(dt&0x000F) / 16.0f; - return t; -} -//---------------------------------------------------------- diff --git a/src/ds18b20.h b/src/ds18b20.h deleted file mode 100644 index 868dbf9..0000000 --- a/src/ds18b20.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef DS18B20_H_ -#define DS18B20_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -//-------------------------------------------------- -#include "stm32f1xx_hal.h" -#include -#include -#include -//-------------------------------------------------- -#define SKIP_ROM 0 -#define NO_SKIP_ROM 1 -//-------------------------------------------------- -#define RESOLUTION_9BIT 0x1F -#define RESOLUTION_10BIT 0x3F -#define RESOLUTION_11BIT 0x5F -#define RESOLUTION_12BIT 0x7F -//-------------------------------------------------- - - -//указать порт, к которому подключены датчики -#define DS_PORT GPIOB -#define GPIO_DS_PIN GPIO_PIN_9 -#define GPIO_DS_PIN_OUT GPIO_ODR_ODR9 -#define GPIO_DS_PIN_READ GPIO_IDR_IDR9 -//-------------------------------------------------- - -void DelayMicr(uint32_t cn); -void port_init(void); -uint8_t ds18b20_init(uint8_t mode); -void ds18b20_MeasureTemperCmd(uint8_t mode, uint8_t DevNum); -void ds18b20_ReadStratcpad(uint8_t mode, uint8_t *Data, uint8_t DevNum); -bool ds18b20_GetSign(uint16_t dt); -float ds18b20_Convert(uint16_t dt); -//-------------------------------------------------- - -#ifdef __cplusplus -} -#endif - -#endif /* DS18B20_H_ */ diff --git a/src/main.cpp b/src/main.cpp index 23ef864..f3166ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,17 +8,7 @@ #include #include #include -#include "ds18b20.h" -#include "BMS_low_level_abstraction.h" - - - - -#define MARKER_FIRST_START 100 // Маркер что flash не пустая - -#define BMS_BATTERY_NUMBER_OF_CELLS 32 // Number of cells in BMS packet -#define BMS_PACKET_HEADER 0xAA55AAFF // from SlaveECU github - +#include ADC_HandleTypeDef hadc1; @@ -28,12 +18,7 @@ UART_HandleTypeDef hDebugUart; UART_HandleTypeDef hBms1Uart; UART_HandleTypeDef hBms2Uart; -//------------------------ Ds18b20 -#define MAX_DS18B20_COUNT 8 // TODO: почему 8?! Вроде по описанию в гуглотаблице максимум 6 должно быть... -uint8_t Dev_ID[MAX_DS18B20_COUNT][8] = {0}; -uint8_t Dev_Cnt; -int8_t temperatures[MAX_DS18B20_COUNT] = {0}; - +TIM_HandleTypeDef htim1; void SystemClock_Config(void); @@ -44,10 +29,7 @@ static void MX_USART1_UART_Init(void); static void MX_USART2_UART_Init(void); static void MX_USART3_UART_Init(void); static void MX_GPIO_Init(void); - - - - +static void MX_TIM1_Init(void); void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) @@ -172,84 +154,6 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) } - - - - - - - - - -/// @brief -void read_ds18b20() -{ - // TODO: We should carefully check the ds18b20 temperature conversion - // I really didn't figure out are this temperature conversions correct or not - // Probably this conversion loses sign of temperature... - // In ideal world we should rewrite all ds18b20 related code into OOP paradigm - - uint8_t dt[8]; - uint16_t raw_temper; - float temper; - - // TODO: Dev_Cnt filled in the ds18b20,cpp as extern variable - // Should rewrite this - for (uint8_t i = 1; i <= Dev_Cnt; i++) - { - ds18b20_MeasureTemperCmd(NO_SKIP_ROM, i); - } - for (uint8_t i = 1; i <= Dev_Cnt; i++) - { - ds18b20_ReadStratcpad(NO_SKIP_ROM, dt, i); - DEBUG_LOG_TOPIC("DS18b", "STRATHPAD %d: %02X %02X %02X %02X %02X %02X %02X %02X\n", - i, dt[0], dt[1], dt[2], dt[3], dt[4], dt[5], dt[6], dt[7]); - - raw_temper = ((uint16_t)dt[1] << 8) | dt[0]; - temper = ds18b20_Convert(raw_temper); - - DEBUG_LOG_TOPIC("DS18b", "Raw t: 0x%04X; t: %s%.2f\n", raw_temper, (ds18b20_GetSign(raw_temper)) ? "-" : "+", temper); - - // int8_t temperatures[ADC_CHANNEL_COUNT + MAX_DS18B20_COUNT]; - // t[0]..t[ADC_CHANNEL_COUNT-1] - external temperature sensors (ADC) - // t[ADC_CHANNEL_COUNT]..t[max] - external temperature sensors (ds18b20) - if (i - 1 < MAX_DS18B20_COUNT) - { - temperatures[i - 1] = (int8_t)(temper); - } - } -} - -/// @brief Initialization of DS18B20 digital termometers -void InitDS18B20() -{ - port_init(); - DEBUG_LOG_TOPIC("DS18b", "Init Status: %d\n", ds18b20_init(NO_SKIP_ROM)); - DEBUG_LOG_TOPIC("DS18b", "Dev count: %d\n", Dev_Cnt); - for (uint8_t i = 1; i <= Dev_Cnt; i++) - { - DEBUG_LOG_TOPIC("DS18b", "Device %d\n", i); - DEBUG_LOG_TOPIC("DS18b", "ROM RAW: %02X %02X %02X %02X %02X %02X %02X %02X\n", - Dev_ID[i - 1][0], Dev_ID[i - 1][1], Dev_ID[i - 1][2], Dev_ID[i - 1][3], - Dev_ID[i - 1][4], Dev_ID[i - 1][5], Dev_ID[i - 1][6], Dev_ID[i - 1][7]); - DEBUG_LOG_TOPIC("DS18b", "Family CODE: 0x%02X\n", Dev_ID[i - 1][0]); - DEBUG_LOG_TOPIC("DS18b", "ROM CODE: 0x%02X%02X%02X%02X%02X%02X\n", Dev_ID[i - 1][6], Dev_ID[i - 1][5], - Dev_ID[i - 1][4], Dev_ID[i - 1][3], Dev_ID[i - 1][2], Dev_ID[i - 1][1]); - DEBUG_LOG_TOPIC("DS18b", "CRC: 0x%02X\n", Dev_ID[i - 1][7]); - } -} - -/// @brief Updates temperature data in temperatures[ADC_CHANNEL_COUNT + MAX_DS18B20_COUNT] -void UpdateTemperatureData() -{ - // int8_t temperatures[ADC_CHANNEL_COUNT + MAX_DS18B20_COUNT]; - // t[0]..t[ADC_CHANNEL_COUNT-1] - external temperature sensors (ADC) - // t[ADC_CHANNEL_COUNT]..t[max] - external temperature sensors (ds18b20) - read_ds18b20(); -} - -/// @brief The application entry point. -/// @retval int int main(void) { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ @@ -265,6 +169,7 @@ int main(void) MX_USART2_UART_Init(); MX_USART3_UART_Init(); MX_GPIO_Init(); + MX_TIM1_Init(); // Сразу после инициализации периферии, иначе программа упадёт, если попробовать включить диод. @@ -277,31 +182,19 @@ int main(void) Leds::Setup(); SPI::Setup(); BMSLogic::Setup(); + OneWire::Setup(); CANLib::Setup(); - InitDS18B20(); - - uint32_t last_tick1 = HAL_GetTick(); - uint32_t current_time = HAL_GetTick(); - while (1) - { - - // Perform ADC & ds18b20 reading with 1 sec period - if (current_time - last_tick1 > 1000) - { - UpdateTemperatureData(); - CANLib::UpdateCANObjects_ExternalTemperature(temperatures, MAX_DS18B20_COUNT); - last_tick1 = current_time; - } - - // don't need to update current_time because it is always updated by Loop() functions - // current_time = HAL_GetTick(); - About::Loop(current_time); - Leds::Loop(current_time); + uint32_t current_time = HAL_GetTick(); + while (1) + { + About::Loop(current_time); + Leds::Loop(current_time); SPI::Loop(current_time); BMSLogic::Loop(current_time); - CANLib::Loop(current_time); - } + OneWire::Loop(current_time); + CANLib::Loop(current_time); + } } void SystemClock_Config(void) @@ -506,3 +399,47 @@ void assert_failed(uint8_t *file, uint32_t line) /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ + +static void MX_TIM1_Init(void) +{ + __HAL_RCC_TIM1_CLK_ENABLE(); + + /* USER CODE BEGIN TIM1_Init 0 */ + + /* USER CODE END TIM1_Init 0 */ + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + /* USER CODE BEGIN TIM1_Init 1 */ + + /* USER CODE END TIM1_Init 1 */ + htim1.Instance = TIM1; + htim1.Init.Prescaler = 63; + htim1.Init.CounterMode = TIM_COUNTERMODE_UP; + htim1.Init.Period = 65535; + htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + htim1.Init.RepetitionCounter = 0; + htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim1) != HAL_OK) + { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) + { + Error_Handler(); + } + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN TIM1_Init 2 */ + + /* USER CODE END TIM1_Init 2 */ + + HAL_TIM_Base_Start(&htim1); + +} \ No newline at end of file diff --git a/src/stm32f1xx_hal_conf.h b/src/stm32f1xx_hal_conf.h index 16419df..6308455 100644 --- a/src/stm32f1xx_hal_conf.h +++ b/src/stm32f1xx_hal_conf.h @@ -64,7 +64,7 @@ /*#define HAL_SMARTCARD_MODULE_ENABLED */ #define HAL_SPI_MODULE_ENABLED /*#define HAL_SRAM_MODULE_ENABLED */ -/*#define HAL_TIM_MODULE_ENABLED */ +#define HAL_TIM_MODULE_ENABLED #define HAL_UART_MODULE_ENABLED /*#define HAL_USART_MODULE_ENABLED */ /*#define HAL_WWDG_MODULE_ENABLED */