/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * *

© Copyright (c) 2020 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "adc.h" #include "crc.h" #include "dac.h" #include "dma.h" #include "iwdg.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include #include #include "log.h" #include "sysdata.h" #include "modbus.h" #include "SEGGER_RTT.h" #include "feeprom.h" #include "tast.h" #include "string.h" #include "precharge.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ typedef enum LOGIC {LOGIC_POSITIV, LOGIC_NEGATIV} logic_t; /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define TAG "MAIN" /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ #ifdef USE_RAM_FUNC uint8_t vectorTableInRAM[192] __attribute__ ((aligned (256))); #endif modbus_t modbusData; sys_data_t sys_data; volatile uint16_t ADC_values[ADC_CHANNELS]; volatile int32_t rawMOSFETsVoltageDrop; volatile int32_t rawContactVoltageDropPlus; volatile int32_t rawContactVoltageDropMinus; int command_parser_is_enabled; volatile int overcurrent_shutdown_is_active = 0; volatile int overload_shutdown_is_active = 0; int low_bat_shutdown_is_active = 0; int temperature_shutdown_is_active = 0; int mosfets_voltagedrop_shutdown_is_active = 0; void (*MOSFETS_Management)(void); // Function pointer that is called in ADC interrupt, depending on the states of LVP&OVP void (*LVP_OVP[LVP_OVP_EVENT_NUM])(void); // Function pointers array that contains "what to do" functions for every combination of LVP&OVP void (*AUTO_Mode)(uint32_t, int); // Function pointer that contains function that is executed when gSwitch is in AUTO mode (depends on DIP switches) logic_t LVP_OVP_logic = LOGIC_NEGATIV; // Default logic is negative int manual_overdrive_is_enabled; uint32_t swdioConnection = UINT32_MAX; // Special variable that contains non-zero number, if SWD-debugger is connected to target void (*InternalGreenLED_Management)(void); // Function pointer that controls Green LED void (*InternalBlueLED_Management)(void); // Function pointer that controls Blue LED void (*InternalRedLED_Management)(void); // Function pointer that controls internal Red LED void (*ExternalGreenLED_Management)(void); // Function pointer that controls external Green LED, which is located inside of the button void (*ExternalRedLED_Management)(void); // Function pointer that controls external Red LED, which is located inside of the button int RS485ActiveMode = 1; // RS485 transsiver is active int auto_recover_from_temp_shutdown_is_enabled; // Automatic reconnect after overtemperature disconnect //uint16_t i_samples[I_RMS_SAMPLES_COUNT] __attribute__((aligned(4))); //uint16_t d_samples[I_RMS_SAMPLES_COUNT]; //uint16_t u_samples[I_RMS_SAMPLES_COUNT]; //volatile int32_t i_samples_counter = 0; uint16_t savedLockKey; volatile uint32_t overcurrent_shutdown_time = 0xFFFFE0C0; volatile uint32_t overload_shutdown_time = 0xFFFFE0C0; void (*Callibration)(void); //void (*Callibration_2)(void); //void (*ActuelKeyManagement)(void); uint16_t keyAccepted = 0; uint16_t savedLockKey; int statDataChanged = 0; volatile uint32_t maxIntegral = UINT32_MAX; void (*InrushCurrentManagement)(void); //#include "raccess.c" extern accessMode_t accessModeTable[ MAX_ADRESS+1 ]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ void SYSDATA_Init(void); void StartUpSequence(void); #ifdef USE_RAM_FUNC void CopyingVectorTableToRAM(void); #endif void DoNothing(); void OpenBothMOSFETSVeryFast(void); void CloseBothMOSFETSVeryFast(void); void OVP_not_present__LVP_not_present(void); void OVP_present__LVP_not_present(void); void OVP_not_present__LVP_present(void); void OVP_present__LVP_present(void); void ADC_OVP_not_present__LVP_not_present(void); void ADC_OVP_present__LVP_not_present(void); void ADC_OVP_not_present__LVP_present(void); void ADC_OVP_present__LVP_present(void); void ShowSlaveAddressOnLED(uint16_t address, GPIO_TypeDef *port, uint16_t pin); inline __attribute__((always_inline)) void MODBUS_Management(void); void DEBUG_print(uint32_t ticks); void HeavyCalculations(uint32_t ticks); void Keys_Management(void); void AUTO_LVP_OVP_Management(uint32_t ticks, int reset); void AUTO_LVP_Management(uint32_t ticks, int reset); void AUTO_OVP_Management(uint32_t ticks, int reset); void DIP_Switches(void); void OVP_ignored__LVP_not_present(void); void OVP_ignored__LVP_present(void); void ADC_OVP_ignored__LVP_not_present(void); void ADC_OVP_ignored__LVP_present(void); void OVP_not_present__LVP_ignored(void); void OVP_present__LVP_ignored(void); void ADC_OVP_not_present__LVP_ignored(void); void ADC_OVP_present__LVP_ignored(void); void ADC_Close_Both_MOSFETs(void); void ADC_Open_Both_MOSFETs(void); void SystemClock_Decrease(void); void EnterPowerSavingMode(void); void ExitPowerSavingMode(void); void StartOffMode(int reset); void LEDs_Management(void); void BlueLEDShortBlinking(void); void TurnBlueLEDOn(void); void TurnExternalGreenLEDOn(void); void TurnExternalGreenLEDOff(void); //void ExternalRedLEDShortBlinking(void); //void ExternalRedLEDVeryShortBlinking(void); void ExternalGreenLEDShortBlinking(void); void OVP_Management_NoAutoreconnect(uint32_t new_time, int reset); void OVP_present__LVP_ignored_NoAutoreconnect(void); void LVP_Management_NoAutoreconnect(uint32_t new_time, int reset); void OVP_ignored__LVP_present_NoAutoreconnect(void); void LVP_OVP_Management_NoAutoreconnect(uint32_t new_time, int reset); void ShortCutCheck(void); void TrueRMSCurrentCalculation(void); void TrueRMSCurrentOfflineCalculation(void); void CurrentOfflineCalculation(void); void CallibrateVoltageDropABMiddlePointOffset(void); void CallibrateControlCurrentVoltageDropOnContactBB(void); void CallibrateCurrentSensorZeroOffsetOnContactBB(void); void ABVoltageDropCalculation(void); inline __attribute__((always_inline)) void OpenBothMOSFETS(void); inline __attribute__((always_inline)) void CloseBothMOSFETS(void); void InrushCurrentDetected(void); void ExternalRedLED1ShortOnThenLongPauseBlinking(void); void ExternalRedLED2ShortOnThenLongPauseBlinking(void); void ExternalRedLED3ShortOnThenLongPauseBlinking(void); void ExternalRedLED4ShortOnThenLongPauseBlinking(void); void ExternalRedLED5ShortOnThenLongPauseBlinking(void); void ExternalRedLED6ShortOnThenLongPauseBlinking(void); void ExternalRedLED2ShortOnThen2LongOnThenLongPauseBlinking(void); void RS485DisableButtonManagement(uint32_t new_time); static void BatteryLowVoltageProtection(/*uint32_t current_time*/); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ #ifdef DEBUG __HAL_RCC_DBGMCU_CLK_ENABLE(); DBG->APBFZ1 |= (DBG_APB_FZ1_DBG_TIM2_STOP | DBG_APB_FZ1_DBG_TIM6_STOP | DBG_APB_FZ1_DBG_TIM7_STOP); DBG->APBFZ2 |= (DBG_APB_FZ2_DBG_TIM14_STOP | DBG_APB_FZ2_DBG_TIM15_STOP | DBG_APB_FZ2_DBG_TIM16_STOP | DBG_APB_FZ2_DBG_TIM17_STOP); #endif command_parser_is_enabled = 1; uint32_t new_time; uint32_t old_time = 0; //uint32_t MIN_MAX_CALCULATION_DELAY = 3000; uint32_t stat_last_time_checked = 0; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ #define MX_IWDG_Init DoNothing // This line helps to eliminate early start of WatchDog timer and keep code reconfigurable by CubeMx /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); MX_CRC_Init(); MX_DAC1_Init(); MX_TIM17_Init(); MX_IWDG_Init(); MX_TIM16_Init(); MX_TIM14_Init(); MX_TIM7_Init(); MX_TIM6_Init(); MX_TIM2_Init(); MX_TIM15_Init(); /* USER CODE BEGIN 2 */ #undef MX_IWDG_Init // Removing previous definition, that helped not to start a watchdog //#ifdef DEBUG //RCC->APBENR1 |= RCC_APBENR1_DBGEN; //DBG->APBFZ1 |= DBG_APB_FZ1_DBG_TIM7_STOP | DBG_APB_FZ1_DBG_TIM2_STOP | DBG_APB_FZ1_DBG_TIM6_STOP; //DBG->APBFZ2 |= DBG_APB_FZ2_DBG_TIM14_STOP | DBG_APB_FZ2_DBG_TIM16_STOP | DBG_APB_FZ2_DBG_TIM17_STOP; //#endif SYSDATA_Init(); if (HAL_TIM_Base_Start(&htim2) != HAL_OK) LOG_E(TAG, "Cannot start TIMER2!"); SEGGER_RTT_printf(0, RTT_CTRL_CLEAR); LOG_I(TAG, "Program started."); switch(DBG->IDCODE & 0xFFF) { case 0x467: LOG_I(TAG, "Device ID: STM32G0B1 or STM32G0C1"); break; case 0x460: LOG_I(TAG, "Device ID: STM32G071 or STM32G081"); break; case 0x456: LOG_I(TAG, "Device ID: STM32G051 or STM32G061"); break; case 0x466: LOG_I(TAG, "Device ID: STM32G031 or STM32G041"); break; default: LOG_I(TAG, "Device ID: unknown"); } SEGGER_RTT_printf(0, "%s: Revision number: 0x%4X\n", TAG, DBG->IDCODE>>16); SEGGER_RTT_printf(0, "Free space for cofiguration in fake EEPROM: %u bytes\n", FEEPROM_ConfigFreeBytes()); SEGGER_RTT_printf(0, "Free space for statistics in fake EEPROM: %u bytes\n", FEEPROM_StatFreeBytes()); SEGGER_RTT_printf(0, "MAX_POSSIBLE_DIFF_TO_MEASURE: %u\n", MAX_POSSIBLE_DIFF_TO_MEASURE); SEGGER_RTT_printf(0, "ADC_BAT_CRITICAL_VOLTAGE: %u\n", ADC_BAT_CRITICAL_VOLTAGE); SEGGER_RTT_printf(0, "CPU Freq: %u Hz\n", HAL_RCC_GetSysClockFreq()); if (HAL_RCC_GetSysClockFreq() < 64000000) { LOG_E(TAG, "CPU speed is not 64MHz!"); LOG_E(TAG, "Trying to restart."); HAL_NVIC_SystemReset(); } StartUpSequence(); #ifdef USE_RAM_FUNC CopyingVectorTableToRAM(); #endif if(FEEPROM_isFirstStart()) { LOG_W(TAG, "First start! Writing default configuration!"); FEEPROM_fullRestore(/*&sys_data*/); FEEPROM_ResetLogData(); } // Fetching configuration from FLASH if (FEEPROM_readConfig(&sys_data)) LOG_E(TAG, "Cannot read configuration from FLASH memory!"); if (FEEPROM_ReadLogData(&sys_data)) LOG_E(TAG, "Cannot read statistcal data from FLASH memory!"); sys_data.s.startup_cnt++; // Start-up counter statDataChanged = 1; maxIntegral = sys_data.s.inrush_max_current_in_adc * sys_data.s.inrush_curr_integral_steps; sys_data.s.copper_v_drop_adc_limit = (sys_data.s.copper_v_drop_adc * 110) / 100; ShowSlaveAddressOnLED(sys_data.s.slave_address, LED_ERROR_GPIO_Port, LED_ERROR_Pin); // Modbus Initialisierung if (sys_data.s.parity_mode == 'e') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_EVEN, &huart1, accessModeTable, &keyAccepted); else if (sys_data.s.parity_mode == 'o') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_ODD, &huart1, accessModeTable, &keyAccepted); else mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_NONE, &huart1, accessModeTable, &keyAccepted); // ADC self-calibration if (HAL_ADC_Stop(&hadc1) == HAL_OK) // Stopping ADC { if (HAL_ADCEx_Calibration_Start (&hadc1) == HAL_OK) { uint32_t calibration_value = HAL_ADCEx_Calibration_GetValue(&hadc1); SEGGER_RTT_printf(0, "%s%s: ADC Calibration value: %u\n", RTT_CTRL_TEXT_BRIGHT_GREEN,TAG, calibration_value | 0x3F); } else LOG_E(TAG, "ADC calibration error!"); } else LOG_E(TAG, "Cannot stop ADC!"); // DAC calibration uint32_t calib_1 = HAL_DACEx_GetTrimOffset(&hdac1, MOSFET_CHANNEL_A); uint32_t calib_2 = HAL_DACEx_GetTrimOffset(&hdac1, MOSFET_CHANNEL_B); SEGGER_RTT_printf(0, "%s: DAC Calibration value for channel 1: %u\n", TAG, calib_1); SEGGER_RTT_printf(0, "%s: DAC Calibration value for channel 2: %u\n", TAG, calib_2); // Before ADC start we must initialize our MOSFETs management function pointer StartOffMode(1); //MOSFETS_Management = &ADC_Open_Both_MOSFETs; // & is optional, but it clearly refers to pointer initialization //sys_data.s.user_button_mode = SWITCH_OFF; // Initial state of the switch if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_values, ADC_CHANNELS) != HAL_OK) LOG_E(TAG, "Cannot start ADC in DMA mode!"); DMA1_Channel1->CCR &= ~DMA_CCR_HTIE; // Disabling Half-Transfer interrupt, because we don't need it // Starting DAC HAL_DAC_Start(&hdac1, MOSFET_CHANNEL_A); HAL_DAC_Start(&hdac1, MOSFET_CHANNEL_B); // Setting new values HAL_DAC_SetValue(&hdac1, MOSFET_CHANNEL_A, DAC_ALIGN_12B_R, DAC_0V); HAL_DAC_SetValue(&hdac1, MOSFET_CHANNEL_B, DAC_ALIGN_12B_R, DAC_0V); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ DIP_Switches(); // Initializing independant watchdog timer (cannot be disabled) //MX_IWDG_Init(); // Due to the fact, that gSwitch always start in OFF state, after start-up // we can enter low power mode, because nothing dangerous happens in OFF mode //EnterPowerSavingMode(); InternalGreenLED_Management = &DoNothing; InternalBlueLED_Management = &BlueLEDShortBlinking; InternalRedLED_Management = &DoNothing; ExternalGreenLED_Management = &DoNothing; ExternalRedLED_Management = &DoNothing; Callibration = &DoNothing; InrushCurrentManagement = &InrushCurrentDetected; while (1) // Main loop { TP4_GPIO_Port->BSRR = TP4_Pin; ABVoltageDropCalculation(); BatteryLowVoltageProtection(); TP4_GPIO_Port->BRR = TP4_Pin; //TIM2->CNT = 0; //CurrentOfflineCalculation(); //TrueRMSCurrentOfflineCalculation(); // Max execution time is //SEGGER_RTT_printf(0, "Function lasts %u cycles\n", TIM2->CNT); MODBUS_Management(); // This function does not rely on time event Keys_Management(); new_time = HAL_GetTick(); // Saving current time if (new_time == old_time) continue; // If tick value hasn't changed since last time, then it is useless to check the rest of conditions below old_time = new_time; // Saving current time value //HAL_IWDG_Refresh(&hiwdg); // 0.5s RESET Callibration(); LEDs_Management(); // Printing DEBUG messages // If SWD connector is not connected, we do not waste time for printing swdioConnection <<= 1; // Preparing space for 1 bit swdioConnection |= HAL_GPIO_ReadPin(SWCLK_Port, SWCLK_Pin); // Sampling SWCLK pin if (swdioConnection) DEBUG_print(new_time); // If debugger is connected, then we show debug messages // Some not extremely urgent values calculations HeavyCalculations(new_time); // Max execution time is 74µs RS485DisableButtonManagement(new_time); //Speicher Log Daten maximal jede Stunde, aber auch nur dann, wenn siche Werte, seit dem letztem mal geändert haben if (new_time - stat_last_time_checked > HOUR_TIME_INTERVALL) { stat_last_time_checked = new_time; LOG_I(TAG, "It is time to save statistical data in Flash memory."); if (statDataChanged) { FEEPROM_StoreLogData(&sys_data); statDataChanged = false; } } static int restartAutoMode = 0; // Checking switch state (Can be changed via Modbus or external button) switch(sys_data.s.user_button_mode) { case SWITCH_OFF: // If button is set to OFF, then we disconnect both MOSFETS break; case SWITCH_ON: // If button is set to ON, then we connect both MOSFETS (Danger!!!) // Checking whether temperature, current and battery voltage are within limits if (temperature_shutdown_is_active == 1) { // If so, opening both MOSFETs (i.e. disconnecting load/charger from battery) #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_Open_Both_MOSFETs; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalRedLED_Management = &ExternalRedLED1ShortOnThenLongPauseBlinking; } else if (overcurrent_shutdown_is_active == 1) ExternalRedLED_Management = &ExternalRedLED2ShortOnThenLongPauseBlinking; else if (mosfets_voltagedrop_shutdown_is_active == 1) ExternalRedLED_Management = &ExternalRedLED3ShortOnThenLongPauseBlinking; else if (overload_shutdown_is_active == 1) ExternalRedLED_Management = &ExternalRedLED4ShortOnThenLongPauseBlinking; break; case SWITCH_AUTO: // Checking whether temperature, current and battery voltage are within limits if (temperature_shutdown_is_active == 1) { if (!restartAutoMode) { // If so, opening both MOSFETs (i.e. disconnecting load/charger from battery) /* #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_Open_Both_MOSFETs; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalRedLED_Management = &ExternalRedLED1ShortOnThenLongPauseBlinking; */ restartAutoMode = 1; } } else if (low_bat_shutdown_is_active == 1) { if (!restartAutoMode) { // If so, opening both MOSFETs (i.e. disconnecting load/charger from battery) #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &DoNothing; OpenBothMOSFETSVeryFast(); sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalRedLED_Management = &ExternalRedLED5ShortOnThenLongPauseBlinking; restartAutoMode = 1; } } else if (overcurrent_shutdown_is_active == 1) { if (!restartAutoMode) { ExternalRedLED_Management = &ExternalRedLED2ShortOnThenLongPauseBlinking; restartAutoMode = 1; } } else if (mosfets_voltagedrop_shutdown_is_active == 1) { if (!restartAutoMode) { ExternalRedLED_Management = &ExternalRedLED3ShortOnThenLongPauseBlinking; restartAutoMode = 1; } } else if (overload_shutdown_is_active == 1) { if (!restartAutoMode) { ExternalRedLED_Management = &ExternalRedLED4ShortOnThenLongPauseBlinking; restartAutoMode = 1; } } else { // Normal operations /** This is a main function that is called in AUTO mode. Which one function is executed depends on DIP switch state. In general, here can be executed 3 functions: 1. AUTO_LVP_Management (OVP is ignored) 2. AUTO_OVP_Management (LVP is ignored) 3. AUTO_LVP_OVP_Management As long as device is in AUTO mode, this function is called periodically. */ AUTO_Mode(new_time, restartAutoMode); restartAutoMode = 0; } break; } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* while (1) */ /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI |RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV8; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1; RCC_OscInitStruct.PLL.PLLN = 16; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } /** Enables the Clock Security System */ HAL_RCC_EnableCSS(); } /* USER CODE BEGIN 4 */ //----------------------------------------------------------------------------- void RS485DisableButtonManagement(uint32_t new_time) { static uint32_t btn_last_time_checked = 0; uint32_t BTN_SCAN_PERIOD = 25; static uint8_t btn_state = 0; static int transition = 1; // Scanning special button on the board, which disables RS485 MODBUS interface if (new_time - btn_last_time_checked > BTN_SCAN_PERIOD) { btn_last_time_checked = new_time; BTN_SCAN_PERIOD = 25; btn_state <<= 1; // If this special button is pressed if (HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin) == BTN_IS_PRESSED) { //BTN_SCAN_PERIOD = 1500; btn_state |= 1; if (btn_state & 0xFF) { //btn_state = 0; // If MODBUS RS485 interface is enabled if (transition) { transition = 0; if (RS485ActiveMode) { // We disable it InternalBlueLED_Management = &TurnBlueLEDOn; RS485ActiveMode = 0; } else { // We enable it InternalBlueLED_Management = &BlueLEDShortBlinking; RS485ActiveMode = 1; } } } } else { btn_state |= 0; if (btn_state == 0) transition = 1; } } } //----------------------------------------------------------------------------- void ABVoltageDropCalculation(void) { static int32_t ursense_voltage_accum = 0; static volatile uint32_t last_time_UabCalculated = 0; static int positive_pulse_found = 0; static volatile uint32_t new_time; new_time = HAL_GetTick(); if (new_time - last_time_UabCalculated > 1) { last_time_UabCalculated = new_time; // Calculating real voltage drop between contacts B and A in mV sys_data.s.ab_raw_adc_value_with_offset = rawMOSFETsVoltageDrop + sys_data.s.ab_middle_point_offset; int32_t temp = ((sys_data.s.ab_raw_adc_value_with_offset) * 2 * MAX_POSSIBLE_DIFF_TO_MEASURE) / ADC_MAX_VALUE - MAX_POSSIBLE_DIFF_TO_MEASURE; // Calculating averaged value for real voltage drop between contacts B and A in mV ursense_voltage_accum -= sys_data.s.ursense_voltage; ursense_voltage_accum += temp; sys_data.s.ursense_voltage = ursense_voltage_accum /16384; if ((sys_data.s.relay_status != RELAY_IS_OPENED)/* || (sys_data.s.relay_status == ONLY_BA_OPENED) || (sys_data.s.relay_status == ONLY_AB_OPENED)*/) { if (!positive_pulse_found) { positive_pulse_found = 1; ursense_voltage_accum = 0; sys_data.s.ursense_voltage = 0; return; } if (sys_data.s.relay_status == ONLY_BA_OPENED) { // Here we can start tracking the voltage drop on MOSFETs if (sys_data.s.ursense_voltage < -(MAX_ALLOWED_MOSFETS_V_DROP + MAX_ALLOWED_MOSFETS_V_DROP_DELTA)) { if (mosfets_voltagedrop_shutdown_is_active == 0) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); OpenBothMOSFETSVeryFast(); MOSFETS_Management = &DoNothing; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); mosfets_voltagedrop_shutdown_is_active = 1; //sys_data.s.device_status |= (1 << ABBA_VOLTAGE_ERROR); sys_data.s.mosfets_voltagedrop_error_cnt++; statDataChanged = 1; } } } else if (sys_data.s.relay_status == ONLY_AB_OPENED) { // Here we can start tracking the voltage drop on MOSFETs if (sys_data.s.ursense_voltage > (MAX_ALLOWED_MOSFETS_V_DROP + MAX_ALLOWED_MOSFETS_V_DROP_DELTA)) { if (mosfets_voltagedrop_shutdown_is_active == 0) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); OpenBothMOSFETSVeryFast(); MOSFETS_Management = &DoNothing; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); mosfets_voltagedrop_shutdown_is_active = 1; //sys_data.s.device_status |= (1 << ABBA_VOLTAGE_ERROR); sys_data.s.mosfets_voltagedrop_error_cnt++; statDataChanged = 1; } } } } else positive_pulse_found = 0; //SEGGER_RTT_printf(0, "Uab = %4d Rstatus = %d\n", sys_data.s.ursense_voltage, sys_data.s.relay_status); } } //----------------------------------------------------------------------------- void mb_save_lock_key(void) { if (sys_data.s.lockKey == savedLockKey) { FEEPROM_storeConfig(&sys_data, false); savedLockKey = sys_data.s.newLockKey; sys_data.s.lockKey = savedLockKey; } if (savedLockKey != 0) { sys_data.s.writeLocked = 1; } else { sys_data.s.writeLocked = 0; } } //----------------------------------------------------------------------------- void SYSDATA_Init(void) { memset(&sys_data, 0, sizeof(sys_data)); sys_data.s.device_type_id = DEVICE_TYPE_ID; sys_data.s.fw_major = FW_VERSION_MAJOR; sys_data.s.fw_minor = FW_VERSION_MINOR; sys_data.s.fw_revision = FW_VERSION_REVISION; sys_data.s.command = 0; sys_data.s.device_status = 0; sys_data.s.ubsenseb_voltage = 1; } //----------------------------------------------------------------------------- void CurrentOfflineCalculation(void) { static uint32_t last_time = 0; uint32_t new_time = HAL_GetTick(); if (new_time - last_time >= 1) { last_time = new_time; // Sliding average calculation of discharge current //current_temperature = (((MAX_TEMP - MIN_TEMP)*((int)ADC_values[TEMP_CHANNEL] - TEMP_SENSOR_ADC_AT_MINUS30))/(TEMP_SENSOR_ADC_AT_PLUS100 - TEMP_SENSOR_ADC_AT_MINUS30)) + MIN_TEMP; //temperature_accum -= sys_data.s.temperature; //temperature_accum += current_temperature; //sys_data.s.temperature = temperature_accum / 32;//>> 5; } } //----------------------------------------------------------------------------- void TrueRMSCurrentOfflineCalculation(void) { static uint32_t last_time = 0; static uint32_t samples_cnt = 0; static uint64_t sum = 0; const uint32_t N = 8; uint32_t new_time = HAL_GetTick(); if (new_time - last_time >= 1) { last_time = new_time; // rawContactVoltageDrop is in the range [-4095, 4095] // Saving it in temporary variable, to prevent corruption of its value in ISR int32_t tmp = rawContactVoltageDropPlus; sum += (tmp * tmp); samples_cnt++; //SEGGER_RTT_printf(0, "[%3u] %5d %8u\n", samples_cnt, tmp, sum); } if (samples_cnt > (CONTROL_CURRENT_A*N)) // Number of samples is picked to correspond to CONTROL_CURRENT_A value. Must be CONTROL_CURRENT_A * N, where N is [1,2,3...] { samples_cnt = 0; //SEGGER_RTT_printf(0, "%u\n", sum); if (sys_data.s.ursense_voltage >= 0) { sys_data.s.current = sqrtf((sum*CONTROL_CURRENT_A)/(sys_data.s.copper_v_drop_adc * sys_data.s.copper_v_drop_adc * N)); } else { sys_data.s.current = -sqrtf((sum*CONTROL_CURRENT_A)/(sys_data.s.copper_v_drop_adc * sys_data.s.copper_v_drop_adc * N)); } //SEGGER_RTT_printf(0, "%d\n", sys_data.s.current); sum = 0; } } //----------------------------------------------------------------------------- /*void TrueRMSCurrentCalculation(void) { static uint64_t total_sum = 0; static uint32_t total_sum_cnt = 0; // Executing this if ISR filled all I_RMS_SAMPLES_COUNT values into i_samples array if (i_samples_counter == I_RMS_SAMPLES_COUNT) { #ifdef DEBUG static uint64_t sum = 0; #else uint64_t sum = 0; //int is_negative; #endif for (int i = 0; i < I_RMS_SAMPLES_COUNT; i++) { #ifdef DEBUG static int32_t shifted_value = 0; shifted_value = i_samples[i] - (ADC_MAX_VALUE>>1); static uint32_t zero_based_value; #else static volatile int32_t shifted_value; shifted_value = i_samples[i] - (ADC_MAX_VALUE>>1); // Delta from middle point of 2047 //uint32_t zero_based_value; #endif //if (shifted_value >= 0) //{ // zero_based_value = shifted_value; // //is_negative = 0; //} //else //{ // zero_based_value = - shifted_value; // //is_negative = 1; //} //static float divider; //divider = ADC_MAX_VALUE * sys_data.s.copper_v_drop * (1 + (COPPER_TEMP_COEFFICIENT * (sys_data.s.temperature - 200))/10); //current_value = (zero_based_value * CONTROL_CURRENT_A * ADC_VREF) / divider; //float divider = ADC_MAX_VALUE * sys_data.s.copper_v_drop * (1 + (COPPER_TEMP_COEFFICIENT * (sys_data.s.temperature - sys_data.s.copper_v_drop_temp))/10); //float divider = sys_data.s.copper_v_drop_adc * (1 + (COPPER_TEMP_COEFFICIENT * (sys_data.s.temperature - sys_data.s.copper_v_drop_temp))/10); static volatile int32_t current_value; current_value = (shifted_value * CONTROL_CURRENT_A) / sys_data.s.copper_v_drop_adc; if (current_value < 0) { if (-current_value > sys_data.s.max_discharge_current) sys_data.s.max_discharge_current = -current_value; else if (-current_value < sys_data.s.min_discharge_current) sys_data.s.min_discharge_current = -current_value; } else { if (current_value > sys_data.s.max_charge_current) sys_data.s.max_charge_current = current_value; else if (current_value < sys_data.s.min_charge_current) sys_data.s.min_charge_current = current_value; } sum += (shifted_value * shifted_value); //char div[50]; //sprintf(div, "%f", divider); //SEGGER_RTT_printf(0, "%u, %u\n", u_samples[i], d_samples[i]); } i_samples_counter = 0; if (total_sum_cnt < I_RMS_SAMPLES_SUM_COUNT) { total_sum += sum; total_sum_cnt++; } else { total_sum_cnt = 0; //SEGGER_RTT_printf(0, "Sum: %d\tTotal: %d\n", sum, total_sum); total_sum = (total_sum * CONTROL_CURRENT_A * CONTROL_CURRENT_A) / (sys_data.s.copper_v_drop_adc * sys_data.s.copper_v_drop_adc); if (sys_data.s.ursense_voltage >= 0) sys_data.s.current = sqrtf(total_sum/(I_RMS_SAMPLES_COUNT * I_RMS_SAMPLES_SUM_COUNT)); else sys_data.s.current = -sqrtf(total_sum/(I_RMS_SAMPLES_COUNT * I_RMS_SAMPLES_SUM_COUNT)); //else sys_data.s.current = sqrtf(total_sum/(I_RMS_SAMPLES_COUNT * I_RMS_SAMPLES_SUM_COUNT)); total_sum = 0; } } }*/ //----------------------------------------------------------------------------- /*#ifdef USE_RAM_FUNC __RAM_FUNC void ShortCutCheck(void) #else void ShortCutCheck(void) #endif { static uint32_t last_time_checked = 0; static int current_integral_calc_started = 0; static uint32_t current_integral = 0; uint32_t current_time = HAL_GetTick(); int32_t current_adc_value = abs(rawContactVoltageDrop - (ADC_MAX_VALUE>>1)); if (current_time - last_time_checked > 1) { last_time_checked = current_time; if (current_adc_value > ADC_VALUE_DELTA_AT_CONTROL_CURRENT_A) { if (current_integral_calc_started) { current_integral += current_adc_value; if (current_integral > (ADC_VALUE_DELTA_AT_INRUSH_CURRENT * 5)) overcurrent_shutdown_is_active = 1; SEGGER_RTT_printf(0, "\t%d\t%d\n", current_adc_value, current_integral); } else { current_integral = 0; current_integral += current_adc_value; current_integral_calc_started = 1; SEGGER_RTT_printf(0, "Overcurrent: %d\n", current_adc_value); SEGGER_RTT_printf(0, "Starting integral calculation:\n"); SEGGER_RTT_printf(0, "\t%d\t%d\n", current_adc_value, current_integral); } } else { if (current_integral_calc_started) { SEGGER_RTT_printf(0, "Stopping integral calculation:\n"); SEGGER_RTT_printf(0, "\t%d\t%d\n", current_adc_value, current_integral); current_integral = 0; current_integral_calc_started = 0; } } } }*/ //----------------------------------------------------------------------------- #ifdef USE_RAM_FUNC void CopyingVectorTableToRAM(void) { uint32_t SrcAddress = SCB->VTOR; uint32_t DstAddress = (uint32_t)vectorTableInRAM; if (HAL_DMA_Start(&hdma_memtomem_dma1_channel2, SrcAddress, DstAddress, 192/4) != HAL_OK) { LOG_E(TAG, "Cannot copy Vector Table from FLASH to RAM! DMA is not ready!"); while(1); } else LOG_I(TAG, "Starting Vector Table copying from FLASH to RAM..."); if (HAL_DMA_PollForTransfer(&hdma_memtomem_dma1_channel2, HAL_DMA_FULL_TRANSFER, 1000) != HAL_OK) { LOG_E(TAG, "Cannot finish copying Vector Table from FLASH to RAM!"); while(1); } else LOG_I(TAG, "Vector Table has been copied from FLASH to RAM."); SCB->VTOR = DstAddress; } #endif //------------------------------------------------------------------------------ void LEDBlink(uint32_t *local_prev_on_time, // last tick counter value unsigned *local_step, // current number of led ignitions unsigned *local_subStep, // corresponds to current phase of led blinking process unsigned local_totalStages, // how many times led should blink uint16_t *local_turnOnTime, // array that contains times for how long led should stay in on state uint16_t *local_turnOffTime, // array that contains times for how long led should stay in off state GPIO_TypeDef *GPIOx, // port name uint16_t GPIO_Pin) // pin number { uint32_t new_time = HAL_GetTick(); if (*local_step < local_totalStages) { switch((*local_subStep)) { case 0: // Phase 0 - turning led on HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_SET); *local_prev_on_time = new_time; (*local_subStep)++; break; case 1: // Phase 1 - turning led off, if it is time if (new_time - *local_prev_on_time > local_turnOnTime[*local_step]) { HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); *local_prev_on_time = new_time; (*local_subStep)++; } break; case 2: // Phase 2 - waiting before returning to phase 0 if (new_time - *local_prev_on_time > local_turnOffTime[*local_step]) { *local_prev_on_time = new_time; *local_subStep = 0; (*local_step)++; } break; } } else *local_step = 0; } //------------------------------------------------------------------------------ void RedLEDBlink(uint16_t *turnOnPeriods, uint16_t *turnOffPeriods, unsigned totalStages) { static uint32_t RedLEDLastTickTime = 0; static unsigned stage = 0; static unsigned subStage = 0; LEDBlink(&RedLEDLastTickTime, &stage, &subStage, totalStages, turnOnPeriods, turnOffPeriods, LED_ERROR_GPIO_Port, LED_ERROR_Pin); } //------------------------------------------------------------------------------ void ExternalRedLEDBlink(uint16_t *turnOnPeriods, uint16_t *turnOffPeriods, unsigned totalStages) { static uint32_t RedLEDLastTickTime = 0; static unsigned stage = 0; static unsigned subStage = 0; LEDBlink(&RedLEDLastTickTime, &stage, &subStage, totalStages, turnOnPeriods, turnOffPeriods, LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin); } //------------------------------------------------------------------------------ void ExternalRedLED2ShortOnThen2LongOnThenLongPauseBlinking(void) { const unsigned stages = 4; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; turnOnTimes[0] = 200; turnOffTimes[0] = 200; turnOnTimes[1] = 200; turnOffTimes[1] = 500; turnOnTimes[2] = 700; turnOffTimes[2] = 500; turnOnTimes[stages - 1] = 700; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED6ShortOnThenLongPauseBlinking(void) { const unsigned stages = 6; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; for (unsigned i = 0; i < stages - 1; i++) { turnOnTimes[i] = 200; turnOffTimes[i] = 200; } turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED5ShortOnThenLongPauseBlinking(void) { const unsigned stages = 5; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; for (unsigned i = 0; i < stages - 1; i++) { turnOnTimes[i] = 200; turnOffTimes[i] = 200; } turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED4ShortOnThenLongPauseBlinking(void) { const unsigned stages = 4; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; for (unsigned i = 0; i < stages - 1; i++) { turnOnTimes[i] = 200; turnOffTimes[i] = 200; } turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED3ShortOnThenLongPauseBlinking(void) { const unsigned stages = 3; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; for (unsigned i = 0; i < stages - 1; i++) { turnOnTimes[i] = 200; turnOffTimes[i] = 200; } turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED2ShortOnThenLongPauseBlinking(void) { const unsigned stages = 2; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; turnOnTimes[0] = 200; turnOffTimes[0] = 200; turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ void ExternalRedLED1ShortOnThenLongPauseBlinking(void) { const unsigned stages = 1; uint16_t turnOnTimes[stages]; uint16_t turnOffTimes[stages]; turnOnTimes[stages - 1] = 200; turnOffTimes[stages - 1] = 2500; ExternalRedLEDBlink(turnOnTimes, turnOffTimes, stages); RedLEDBlink(turnOnTimes, turnOffTimes, stages); } //------------------------------------------------------------------------------ /*void ExternalRedLEDVeryShortBlinking(void) { static uint32_t old_on_time = 0; static uint32_t led_is_turned_on = 0; uint32_t new_time; new_time = HAL_GetTick(); if (!led_is_turned_on && (new_time - old_on_time > 200)) { HAL_GPIO_WritePin(LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin, GPIO_PIN_SET); // External Red LED on button led_is_turned_on = 1; old_on_time = new_time; } else if (led_is_turned_on && (new_time - old_on_time > 200)) { HAL_GPIO_WritePin(LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin, GPIO_PIN_RESET); led_is_turned_on = 0; old_on_time = new_time; } }*/ //------------------------------------------------------------------------------ /*void ExternalRedLEDShortBlinking(void) { static uint32_t old_on_time = 0; static uint32_t led_is_turned_on = 0; uint32_t new_time; new_time = HAL_GetTick(); if (!led_is_turned_on && (new_time - old_on_time > 800)) { HAL_GPIO_WritePin(LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin, GPIO_PIN_SET); // External Red LED on button led_is_turned_on = 1; old_on_time = new_time; } else if (led_is_turned_on && (new_time - old_on_time > 200)) { HAL_GPIO_WritePin(LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin, GPIO_PIN_RESET); led_is_turned_on = 0; old_on_time = new_time; } }*/ //------------------------------------------------------------------------------ void TurnExternalRedLEDOff(void) { HAL_GPIO_WritePin(LED_SW_ERROR_GPIO_Port, LED_SW_ERROR_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_ERROR_GPIO_Port, LED_ERROR_Pin, GPIO_PIN_RESET); ExternalRedLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void ExternalGreenLEDShortBlinking(void) { static uint32_t old_on_time = 0; static uint32_t led_is_turned_on = 0; uint32_t new_time; new_time = HAL_GetTick(); if (!led_is_turned_on && (new_time - old_on_time > 800)) { HAL_GPIO_WritePin(LED_SW_STATE_GPIO_Port, LED_SW_STATE_Pin, GPIO_PIN_SET); // External Green LED on button led_is_turned_on = 1; old_on_time = new_time; } else if (led_is_turned_on && (new_time - old_on_time > 200)) { HAL_GPIO_WritePin(LED_SW_STATE_GPIO_Port, LED_SW_STATE_Pin, GPIO_PIN_RESET); led_is_turned_on = 0; old_on_time = new_time; } } //------------------------------------------------------------------------------ void TurnExternalGreenLEDOff(void) { HAL_GPIO_WritePin(LED_SW_STATE_GPIO_Port, LED_SW_STATE_Pin, GPIO_PIN_RESET); ExternalGreenLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void TurnExternalGreenLEDOn(void) { HAL_GPIO_WritePin(LED_SW_STATE_GPIO_Port, LED_SW_STATE_Pin, GPIO_PIN_SET); ExternalGreenLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void GreenLEDShortBlinking(void) { static uint32_t old_on_time = 0; static uint32_t led_is_turned_on = 0; uint32_t new_time; // Blue LED blinking (950ms - OFF, 50ms - ON) new_time = HAL_GetTick(); if (!led_is_turned_on && (new_time - old_on_time > 950)) { HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_SET); led_is_turned_on = 1; old_on_time = new_time; } else if (led_is_turned_on && (new_time - old_on_time > 50)) { HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_RESET); led_is_turned_on = 0; old_on_time = new_time; } } //------------------------------------------------------------------------------ void TurnGreenLEDOff(void) { HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_RESET); InternalGreenLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void TurnGreenLEDOn(void) { HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_SET); InternalGreenLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void TurnBlueLEDOn(void) { HAL_GPIO_WritePin(LED_FUNCTION_GPIO_Port, LED_FUNCTION_Pin, GPIO_PIN_SET); InternalBlueLED_Management = &DoNothing; } //------------------------------------------------------------------------------ void BlueLEDShortBlinking(void) { static uint32_t old_on_time = 0; static uint32_t led_is_turned_on = 0; uint32_t new_time; // Blue LED blinking (950ms - OFF, 50ms - ON) new_time = HAL_GetTick(); if (!led_is_turned_on && (new_time - old_on_time > 950)) { HAL_GPIO_WritePin(LED_FUNCTION_GPIO_Port, LED_FUNCTION_Pin, GPIO_PIN_SET); led_is_turned_on = 1; old_on_time = new_time; } else if (led_is_turned_on && (new_time - old_on_time > 50)) { HAL_GPIO_WritePin(LED_FUNCTION_GPIO_Port, LED_FUNCTION_Pin, GPIO_PIN_RESET); led_is_turned_on = 0; old_on_time = new_time; } } //------------------------------------------------------------------------------ void LEDs_Management(void) { InternalGreenLED_Management(); InternalBlueLED_Management(); InternalRedLED_Management(); ExternalGreenLED_Management(); ExternalRedLED_Management(); } //------------------------------------------------------------------------------ void ExitPowerSavingMode(void) { //HAL_GPIO_WritePin(DISABLE_VBOOST_GPIO_Port, DISABLE_VBOOST_Pin, GPIO_PIN_SET); // Turning VBOOST voltage generator on //HAL_PWREx_DisableLowPowerRunMode(); //HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //SystemClock_Config(); /*modbusData.uart->Instance->PRESC = UART_PRESCALER_DIV8; // Modbus Initialisierung if (sys_data.s.parity_mode == 'e') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_EVEN, &huart1); else if (sys_data.s.parity_mode == 'o') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_ODD, &huart1); else mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_NONE, &huart1);*/ } //------------------------------------------------------------------------------ void EnterPowerSavingMode(void) { //SystemClock_Decrease(); // Reduce the System clock to 2 MHz //HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2); // Set regulator voltage to scale 2 //HAL_PWREx_EnableLowPowerRunMode(); // Enter LP RUN Mode } //------------------------------------------------------------------------------ void SystemClock_Decrease(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitTypeDef RCC_OscInitStruct = {0}; /* Select HSI as system clock source */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { /* Initialization Error */ Error_Handler(); } /* Modify HSI to HSI DIV8 */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV8; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF; if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { /* Initialization Error */ Error_Handler(); } } //------------------------------------------------------------------------------ void DIP_Switches(void) { // Checking positions of DIP switches if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_OFF)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 0 (LVP only. Active level - LOW) LOG_I(TAG, "Mode 0 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_LVP_Management; // Assigning default functions to function pointers array, depending on LVP state LVP_OVP[0] = &OVP_ignored__LVP_not_present; // 0 LVP_OVP[1] = &OVP_ignored__LVP_present; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.ovp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 0 | (1 << 3); } else { // Modus 0 (LVP only. Active level - LOW) LOG_I(TAG, "Mode 0 is selected. Auto-reconnect is OFF."); AUTO_Mode = &LVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on LVP state LVP_OVP[0] = &OVP_ignored__LVP_not_present; // 0 LVP_OVP[1] = &OVP_ignored__LVP_present_NoAutoreconnect; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.ovp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 0; } } else if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_ON) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_OFF)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 1 (OVP only. Active level - LOW) LOG_I(TAG, "Mode 1 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_OVP_Management; // Assigning default functions to function pointers array, depending on OVP state LVP_OVP[0] = &OVP_not_present__LVP_ignored; // 0 LVP_OVP[1] = &OVP_present__LVP_ignored; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.lvp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 1 | (1 << 3); } else { // Modus 1 (OVP only. Active level - LOW) LOG_I(TAG, "Mode 1 is selected. Auto-reconnect is OFF."); AUTO_Mode = &OVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on OVP state LVP_OVP[0] = &OVP_not_present__LVP_ignored; // 0 LVP_OVP[1] = &OVP_present__LVP_ignored_NoAutoreconnect; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.lvp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 1; } } else if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_ON) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_OFF)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 2 (LVP only. Active level - HIGH) LOG_I(TAG, "Mode 2 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_LVP_Management; // Assigning default functions to function pointers array, depending on LVP state LVP_OVP[0] = &OVP_ignored__LVP_not_present; // 0 LVP_OVP[1] = &OVP_ignored__LVP_present; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.ovp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 2 | (1 << 3); } else { // Modus 2 (LVP only. Active level - HIGH) LOG_I(TAG, "Mode 2 is selected. Auto-reconnect is OFF."); AUTO_Mode = &LVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on LVP state LVP_OVP[0] = &OVP_ignored__LVP_not_present; // 0 LVP_OVP[1] = &OVP_ignored__LVP_present_NoAutoreconnect; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.ovp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 2; } } else if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_ON) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_ON) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_OFF)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 3 (OVP only. Active level - HIGH) LOG_I(TAG, "Mode 3 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_OVP_Management; // Assigning default functions to function pointers array, depending on OVP state LVP_OVP[0] = &OVP_not_present__LVP_ignored; // 0 LVP_OVP[1] = &OVP_present__LVP_ignored; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.lvp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 3 | (1 << 3); } else { // Modus 3 (OVP only. Active level - HIGH) LOG_I(TAG, "Mode 3 is selected. Auto-reconnect is OFF."); AUTO_Mode = &OVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on OVP state LVP_OVP[0] = &OVP_not_present__LVP_ignored; // 0 LVP_OVP[1] = &OVP_present__LVP_ignored_NoAutoreconnect; // 1 LVP_OVP[2] = LVP_OVP[3] = &DoNothing; // Not used //sys_data.s.lvp_state = 2; // Ignored state LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 3; } } else if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_ON)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 4 (LVP & OVP. Active level - LOW. With autoreconnect) LOG_I(TAG, "Mode 4 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_LVP_OVP_Management; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 4 | (1 << 3); } else { // Modus 4 (LVP & OVP. Active level - LOW. Without autoreconnect) LOG_I(TAG, "Mode 4 is selected. Auto-reconnect is OFF."); AUTO_Mode = &LVP_OVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 4; } } else if ((HAL_GPIO_ReadPin(DIP0_GPIO_Port, DIP0_Pin) == DIP_IS_ON) && (HAL_GPIO_ReadPin(DIP1_GPIO_Port, DIP1_Pin) == DIP_IS_OFF) && (HAL_GPIO_ReadPin(DIP2_GPIO_Port, DIP2_Pin) == DIP_IS_ON)) { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 5 (LVP & OVP. Active level - HIGH. With autoreconnect) LOG_I(TAG, "Mode 5 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_LVP_OVP_Management; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 5 | (1 << 3); } else { // Modus 5 (LVP & OVP. Active level - HIGH. Without autoreconnect) LOG_I(TAG, "Mode 5 is selected. Auto-reconnect is OFF."); AUTO_Mode = &LVP_OVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_POSITIV; sys_data.s.dip_mode = 5; } } else // The rest of the combinations { // Auto-reconnect DIP-switch if (HAL_GPIO_ReadPin(DIP3_GPIO_Port, DIP3_Pin) == DIP_IS_ON) { // Modus 4 (LVP & OVP. Active level - LOW. With autoreconnect) LOG_I(TAG, "Illegal Mode is selected. Default Mode 4 is selected. Auto-reconnect is ON."); AUTO_Mode = &AUTO_LVP_OVP_Management; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 4 | (1 << 3); } else { // Modus 4 (LVP & OVP. Active level - LOW. Without autoreconnect) LOG_I(TAG, "Illegal Mode is selected. Default Mode 4 is selected. Auto-reconnect is OFF."); AUTO_Mode = &LVP_OVP_Management_NoAutoreconnect; // Assigning default functions to function pointers array, depending on LVP and OVP events combinations LVP_OVP[0] = &OVP_not_present__LVP_not_present; // 00 - 0 LVP_OVP[1] = &OVP_not_present__LVP_present; // 01 - 1 LVP_OVP[2] = &OVP_present__LVP_not_present; // 10 - 2 LVP_OVP[3] = &OVP_present__LVP_present; // 11 - 3 LVP_OVP_logic = LOGIC_NEGATIV; sys_data.s.dip_mode = 4; } } if (HAL_GPIO_ReadPin(DIP4_GPIO_Port, DIP4_Pin) == DIP_IS_ON) { manual_overdrive_is_enabled = 1; // Manual overdrive sys_data.s.dip_mode |= (1 << 4); } else manual_overdrive_is_enabled = 0; if (HAL_GPIO_ReadPin(DIP5_GPIO_Port, DIP5_Pin) == DIP_IS_ON) { auto_recover_from_temp_shutdown_is_enabled = 1; sys_data.s.dip_mode |= (1 << 5); } else auto_recover_from_temp_shutdown_is_enabled = 0; // At the end of this function we can DeInit all these ports // to minimize power consumption HAL_GPIO_DeInit(DIP0_GPIO_Port, DIP0_Pin); HAL_GPIO_DeInit(DIP1_GPIO_Port, DIP1_Pin); HAL_GPIO_DeInit(DIP2_GPIO_Port, DIP2_Pin); HAL_GPIO_DeInit(DIP3_GPIO_Port, DIP3_Pin); HAL_GPIO_DeInit(DIP4_GPIO_Port, DIP4_Pin); HAL_GPIO_DeInit(DIP5_GPIO_Port, DIP5_Pin); HAL_GPIO_DeInit(DIP6_GPIO_Port, DIP6_Pin); HAL_GPIO_DeInit(DIP7_GPIO_Port, DIP7_Pin); } //------------------------------------------------------------------------------ void OVP_Management_NoAutoreconnect(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM>>1])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM>>1] = {10, 3000}; // Delays in ms for LVP events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM>>1] = {1, 100}; // Delays in ms for repeating LVP events static uint32_t OVP_SCAN_PERIOD = 1; static uint32_t ovp_last_time_checked = 0; static int lastIdx = -1; // Impossible index, to make initial assignment // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = -1; return; } if (new_time - ovp_last_time_checked > OVP_SCAN_PERIOD) { // Saving time ovp_last_time_checked = new_time; // Reading the state of LVP pin GPIO_PinState current_OVP_state = HAL_GPIO_ReadPin(OVP_IN_GPIO_Port, OVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) current_OVP_state = !current_OVP_state; // Creating index from LVP values: 0 and 1 int Idx = current_OVP_state; // If new value of LVP sygnal differs from old one, then we must update our function pointer if (Idx > lastIdx) { WhatToDo[Idx] = LVP_OVP[Idx]; // When state of LVP changes, we introduce a delay, "debouncing" OVP_SCAN_PERIOD = newEventDelay[Idx]; } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate OVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the state of the LVP pin, calling specific function, periodically WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ void AUTO_OVP_Management(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM>>1])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM>>1] = {10, 3000}; // Delays in ms for LVP events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM>>1] = {1, 100}; // Delays in ms for repeating LVP events static uint32_t OVP_SCAN_PERIOD = 1; static uint32_t ovp_last_time_checked = 0; static unsigned int lastIdx = 2; // Impossible index, to make initial assignment // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = 2; return; } if (new_time - ovp_last_time_checked > OVP_SCAN_PERIOD) { // Saving time ovp_last_time_checked = new_time; // Reading the state of LVP pin GPIO_PinState current_OVP_state = HAL_GPIO_ReadPin(OVP_IN_GPIO_Port, OVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) current_OVP_state = !current_OVP_state; // Creating index from LVP values: 0 and 1 unsigned int Idx = current_OVP_state; // If new value of LVP sygnal differs from old one, then we must update our function pointer if (Idx != lastIdx) { WhatToDo[Idx] = LVP_OVP[Idx]; // When state of LVP changes, we introduce a delay, "debouncing" OVP_SCAN_PERIOD = newEventDelay[Idx]; } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate OVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the state of the LVP pin, calling specific function, periodically WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ void LVP_Management_NoAutoreconnect(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM>>1])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM>>1] = {10, 3000}; // Delays in ms for LVP events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM>>1] = {1, 100}; // Delays in ms for repeating LVP events static uint32_t LVP_SCAN_PERIOD = 1; static uint32_t lvp_last_time_checked = 0; static int lastIdx = -1; // Impossible index, to make initial assignment // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = -1; return; } if (new_time - lvp_last_time_checked > LVP_SCAN_PERIOD) { // Saving time lvp_last_time_checked = new_time; // Reading the state of LVP pin GPIO_PinState current_LVP_state = HAL_GPIO_ReadPin(LVP_IN_GPIO_Port, LVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) current_LVP_state = !current_LVP_state; // Creating index from LVP values: 0 and 1 int Idx = current_LVP_state; // If new value of LVP sygnal differs from old one, then we must update our function pointer if (Idx > lastIdx) { WhatToDo[Idx] = LVP_OVP[Idx]; // When state of LVP changes, we introduce a delay, "debouncing" LVP_SCAN_PERIOD = newEventDelay[Idx]; } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate LVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the state of the LVP pin, calling specific function, periodically WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ void AUTO_LVP_Management(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM>>1])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM>>1] = {10, 3000}; // Delays in ms for LVP events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM>>1] = {1, 100}; // Delays in ms for repeating LVP events static uint32_t LVP_SCAN_PERIOD = 1; static uint32_t lvp_last_time_checked = 0; static unsigned int lastIdx = 2; // Impossible index, to make initial assignment // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = 2; return; } if (new_time - lvp_last_time_checked > LVP_SCAN_PERIOD) { // Saving time lvp_last_time_checked = new_time; // Reading the state of LVP pin GPIO_PinState current_LVP_state = HAL_GPIO_ReadPin(LVP_IN_GPIO_Port, LVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) current_LVP_state = !current_LVP_state; // Creating index from LVP values: 0 and 1 unsigned int Idx = current_LVP_state; // If new value of LVP sygnal differs from old one, then we must update our function pointer if (Idx != lastIdx) { WhatToDo[Idx] = LVP_OVP[Idx]; // When state of LVP changes, we introduce a delay, "debouncing" LVP_SCAN_PERIOD = newEventDelay[Idx]; } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate LVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the state of the LVP pin, calling specific function, periodically WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ void LVP_OVP_Management_NoAutoreconnect(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM] = {10, 3000, 3000, 3000}; // Delays in ms for new LVP&OVP combination events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM] = {1, 100, 100, 100}; // Delays in ms for repeating LVP&OVP combination events static uint32_t LVP_OVP_SCAN_PERIOD = 1; static uint32_t lvp_ovp_last_time_checked = 0; static int lastIdx = -1; // Impossible index, to make initial assignment static int ovp_lvp_flag = 0; // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = -1; ovp_lvp_flag = 0; return; } if (new_time - lvp_ovp_last_time_checked > LVP_OVP_SCAN_PERIOD) { // Saving time lvp_ovp_last_time_checked = new_time; // Reading the states of OVP&LVP pins GPIO_PinState current_OVP_state = HAL_GPIO_ReadPin(OVP_IN_GPIO_Port, OVP_IN_Pin); GPIO_PinState current_LVP_state = HAL_GPIO_ReadPin(LVP_IN_GPIO_Port, LVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) { current_OVP_state = !current_OVP_state; current_LVP_state = !current_LVP_state; } // Creating index from OVP and LVP values combinations: 0, 1, 2 and 3 int Idx = (current_OVP_state << 1) | current_LVP_state; // Checking previous combination of OVP&LVP if (Idx != lastIdx) { if (!ovp_lvp_flag) { if (Idx > 0) ovp_lvp_flag = 1; WhatToDo[Idx] = LVP_OVP[Idx]; // When states of OVP&LVP changes, we introduce a delay, "debouncing" LVP_OVP_SCAN_PERIOD = newEventDelay[Idx]; } } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate LVP_OVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the combination of the OVP and LVP states, calling specific function, periodically if (WhatToDo[Idx]) WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ void AUTO_LVP_OVP_Management(uint32_t new_time, int reset) { static void (*WhatToDo[LVP_OVP_EVENT_NUM])(void); static const uint32_t newEventDelay[LVP_OVP_EVENT_NUM] = {10, 3000, 3000, 3000}; // Delays in ms for new LVP&OVP combination events static const uint32_t repeatEventDelay[LVP_OVP_EVENT_NUM] = {1, 100, 100, 100}; // Delays in ms for repeating LVP&OVP combination events static uint32_t LVP_OVP_SCAN_PERIOD = 1; static uint32_t lvp_ovp_last_time_checked = 0; static unsigned int lastIdx = 4; // Impossible index, to make initial assignment // This helps to reinitialize AUTO mode when exiting OFF mode with button or modbus if (reset) { lastIdx = 4; return; } if (new_time - lvp_ovp_last_time_checked > LVP_OVP_SCAN_PERIOD) { // Saving time lvp_ovp_last_time_checked = new_time; // Reading the states of OVP&LVP pins GPIO_PinState current_OVP_state = HAL_GPIO_ReadPin(OVP_IN_GPIO_Port, OVP_IN_Pin); GPIO_PinState current_LVP_state = HAL_GPIO_ReadPin(LVP_IN_GPIO_Port, LVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) { current_OVP_state = !current_OVP_state; current_LVP_state = !current_LVP_state; } // Creating index from OVP and LVP values combinations: 0, 1, 2 and 3 unsigned int Idx = (current_OVP_state << 1) | current_LVP_state; // If new combination of OVP&LVP sygnals differ from old one, then we must update our function pointer if (Idx != lastIdx) { WhatToDo[Idx] = LVP_OVP[Idx]; // When states of OVP&LVP changes, we introduce a delay, "debouncing" LVP_OVP_SCAN_PERIOD = newEventDelay[Idx]; } else { WhatToDo[Idx] = &DoNothing; // When previous state is the same like current, then we do nothing with slower rate LVP_OVP_SCAN_PERIOD = repeatEventDelay[Idx]; } // Depending on the combination of the OVP and LVP states, calling specific function, periodically WhatToDo[Idx](); lastIdx = Idx; } } //------------------------------------------------------------------------------ static uint32_t last_time_started = 0; void StartAutoMode(void) { uint32_t current_time = HAL_GetTick(); // Checking whether we are already in this mode if ((sys_data.s.user_button_mode != SWITCH_AUTO) && (sys_data.s.user_button_mode != SWITCH_ON)) { // We should not allow to start this mode very often if (current_time - last_time_started > 1000) { if ((current_time - overload_shutdown_time > 10000) && (current_time - overcurrent_shutdown_time > 10000)) { HAL_TIM_Base_Stop_IT(&ON_MODE_AUTO_OFF_TIMER); last_time_started = current_time; // Turning VBOOST voltage generator on //HAL_GPIO_WritePin(DISABLE_VBOOST_GPIO_Port, DISABLE_VBOOST_Pin, GPIO_PIN_SET); // After 250ms of VBOOST stabilization time, we turn on MOSFETs regulation //HAL_TIM_Base_Stop_IT(&VBOOST_ON_TIMER); __HAL_TIM_CLEAR_FLAG(&VBOOST_ON_TIMER, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&VBOOST_ON_TIMER, 0); HAL_TIM_Base_Start_IT(&VBOOST_ON_TIMER); //CLEAR_BIT(&hadc1->State, HAL_ADC_STATE_AWD2); //CLEAR_BIT(&hadc1->State, HAL_ADC_STATE_AWD3); #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF //__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD2); //__HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD3); //NVIC_ClearPendingIRQ(ADC1_COMP_IRQn); HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn); #endif sys_data.s.switch_cnt++; statDataChanged = 1; //HAL_GPIO_WritePin(FET_PULLDOWN_A_GPIO_Port, FET_PULLDOWN_A_Pin, GPIO_PIN_RESET); //HAL_GPIO_WritePin(FET_PULLDOWN_B_GPIO_Port, FET_PULLDOWN_B_Pin, GPIO_PIN_RESET); } } } } //------------------------------------------------------------------------------ void StartOffMode(int reset) { uint32_t current_time = HAL_GetTick(); // Checking whether we are allowed to enter this mode //if ((sys_data.s.user_button_mode == SWITCH_ON) || (sys_data.s.user_button_mode == SWITCH_AUTO) || reset) //{ if ((current_time - last_time_started > 500) || reset) { HAL_TIM_Base_Stop_IT(&ON_MODE_AUTO_OFF_TIMER); if (sys_data.s.user_button_mode != SWITCH_OFF) last_time_started = current_time; sys_data.s.user_button_mode = SWITCH_OFF; #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_Open_Both_MOSFETs; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &TurnExternalGreenLEDOff; ExternalRedLED_Management = &TurnExternalRedLEDOff; InternalGreenLED_Management = &TurnGreenLEDOff; // After 100ms we turn off VBOOST voltage for power saving //HAL_TIM_Base_Stop_IT(&VBOOST_OFF_TIMER); __HAL_TIM_CLEAR_FLAG(&VBOOST_OFF_TIMER, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&VBOOST_OFF_TIMER, 0); HAL_TIM_Base_Start_IT(&VBOOST_OFF_TIMER); #ifndef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD2); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD3); NVIC_ClearPendingIRQ(ADC1_COMP_IRQn); HAL_NVIC_EnableIRQ(ADC1_COMP_IRQn); #endif overcurrent_shutdown_is_active = 0; overload_shutdown_is_active = 0; temperature_shutdown_is_active = 0; mosfets_voltagedrop_shutdown_is_active = 0; sys_data.s.last_shortcut_during_charge = 0; sys_data.s.last_shortcut_during_discharge = 0; } //} } //------------------------------------------------------------------------------ void StartOnMode(void) { //static uint32_t last_time_started = 0; //uint32_t current_time = HAL_GetTick(); // Checking whether we are already in this mode if ((sys_data.s.user_button_mode != SWITCH_ON) && (sys_data.s.user_button_mode != SWITCH_AUTO)) { if (manual_overdrive_is_enabled) { // We should not allow to start this mode very often //if (current_time - last_time_started > 3000) //{ //last_time_started = current_time; last_time_started = HAL_GetTick(); //HAL_GPIO_WritePin(DISABLE_VBOOST_GPIO_Port, DISABLE_VBOOST_Pin, VBOOST_ENABLE); // After 250ms of VBOOST stabilization time, we turn on MOSFETs On mode __HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&htim6, 0); HAL_TIM_Base_Start_IT(&htim6); // To prevent some unintentional damage to cells we turn this mode off after 1 minute __HAL_TIM_CLEAR_FLAG(&htim16, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&htim16, 0); HAL_TIM_Base_Start_IT(&htim16); sys_data.s.switch_cnt++; statDataChanged = 1; //HAL_GPIO_WritePin(FET_PULLDOWN_A_GPIO_Port, FET_PULLDOWN_A_Pin, GPIO_PIN_RESET); //HAL_GPIO_WritePin(FET_PULLDOWN_B_GPIO_Port, FET_PULLDOWN_B_Pin, GPIO_PIN_RESET); //sys_data.s.user_button_mode = SWITCH_ON; // We disable shortcut detecting interrupt //HAL_NVIC_DisableIRQ(ADC1_COMP_IRQn); //} } } } //------------------------------------------------------------------------------ void Keys_Management(void) { static uint32_t last_time_checked = 0; uint32_t current_time = HAL_GetTick(); if (current_time - last_time_checked >= 1) { last_time_checked = current_time; checkKeys(); if (get_key_short(SW_ON_Pin)) { LOG_I(TAG, "UP button is pressed."); StartAutoMode(); } else if (get_key_long(SW_ON_Pin)) { LOG_I(TAG, "UP button is long-pressed."); StartOnMode(); } else if (get_key_short(SW_OFF_Pin)) { LOG_I(TAG, "DOWN button is pressed."); StartOffMode(0); } } } //------------------------------------------------------------------------------ static inline __attribute__((always_inline)) void CalculatingSwitchSideVoltage(void) { static int32_t ubsensea_voltage_accum = 0; // Calculatiing and averaging switch side voltage int32_t temp = ADC_values[U_SW_CHANNEL]; // Converting ADC value into int32_t type temp = (temp*ADC_VREF)>>ADC_RESOLUTION; // Getting voltage value on ADC input [0:3000]mV #ifdef VARIANT_24V temp = (temp*(R19 + R20 + R27 + R26))/R26; #else temp = (temp*(R25 + R24))/R24; // Getting voltage value on contact A [0:17100]mV #endif ubsensea_voltage_accum -= sys_data.s.ubsensea_voltage; if (ubsensea_voltage_accum < 0) ubsensea_voltage_accum = 0; ubsensea_voltage_accum += temp; sys_data.s.ubsensea_voltage = ubsensea_voltage_accum / 8; } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void CalculatingMinMaxVoltagesForContactA(void) { // Calculating MIN & MAX voltage values for contact A if (sys_data.s.ubsensea_voltage > sys_data.s.max_ubsensea_voltage) sys_data.s.max_ubsensea_voltage = sys_data.s.ubsensea_voltage; else if (sys_data.s.ubsensea_voltage < sys_data.s.min_ubsensea_voltage) sys_data.s.min_ubsensea_voltage = sys_data.s.ubsensea_voltage; } //------------------------------------------------------------------------------ static inline __attribute__((always_inline)) void CalculatingAndAveragingVoltageOnContactB(void) { const uint32_t AVG_VALUE = 64U; static int32_t ubsenseb_voltage_accum = U_BAT_RECOVERY_VOLTAGE_mV * AVG_VALUE; // << ubsenseb_voltage_accum_avg; // Special case for Battery undervoltage condition. During normal operation ubsenseb_voltage will never be zero //if (sys_data.s.ubsenseb_voltage == 0) ubsenseb_voltage_accum = 0; // Calculating and averaging battery side voltage int32_t temp = ADC_values[U_BAT_CHANNEL]; temp = (temp*ADC_VREF)>>ADC_RESOLUTION; // Getting voltage value on ADC input [0:3000]mV #ifdef VARIANT_24V temp = (temp*(R23 + R24 + R25 + R33))/R33; // Getting voltage value on contact B []mV #else temp = (temp*(R23 + R22))/R22; // Getting voltage value on contact B [0:17100]mV #endif ubsenseb_voltage_accum -= sys_data.s.ubsenseb_voltage; if (ubsenseb_voltage_accum < 0) ubsenseb_voltage_accum = 0; ubsenseb_voltage_accum += temp; sys_data.s.ubsenseb_voltage = ubsenseb_voltage_accum / AVG_VALUE; //>> ubsenseb_voltage_accum_avg; } //------------------------------------------------------------------------------ static inline __attribute__((always_inline)) void TestingForLowVoltageShutdown(void) { const uint32_t REACTIVATION_DELAY_ms = 10000; static uint32_t low_bat_event_last_time; // If voltage on contact B is lower than critical one, then we must initiate "low voltage shutdown" if (sys_data.s.ubsenseb_voltage < U_BAT_CRITICAL_VOLTAGE_mV) { if (low_bat_shutdown_is_active == 0) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &DoNothing; OpenBothMOSFETSVeryFast(); sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalRedLED_Management = &ExternalRedLED5ShortOnThenLongPauseBlinking; low_bat_shutdown_is_active = 1; sys_data.s.device_status |= (1 << LOWBAT_ERROR); sys_data.s.lowbat_error_cnt++; statDataChanged = 1; low_bat_event_last_time = HAL_GetTick(); } } else if (sys_data.s.ubsenseb_voltage > U_BAT_RECOVERY_VOLTAGE_mV) { if (low_bat_event_last_time + REACTIVATION_DELAY_ms < HAL_GetTick()) { low_bat_shutdown_is_active = 0; sys_data.s.device_status &= ~(1 << LOWBAT_ERROR); } } /*if (low_bat_shutdown_is_active == 1) { } if (sys_data.s.ubsenseb_voltage > U_BAT_RECOVERY_VOLTAGE_mV) { low_bat_shutdown_is_active = 0; sys_data.s.device_status &= ~(1 << LOWBAT_ERROR); }*/ } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void CalculatingMinMaxVoltagesForContactB(void) { // Calculating MIN & MAX voltage values for contact B if (sys_data.s.ubsenseb_voltage > sys_data.s.max_ubsenseb_voltage) sys_data.s.max_ubsenseb_voltage = sys_data.s.ubsenseb_voltage; else if (sys_data.s.ubsenseb_voltage < sys_data.s.min_ubsenseb_voltage) sys_data.s.min_ubsenseb_voltage = sys_data.s.ubsenseb_voltage; } //------------------------------------------------------------------------------ static inline __attribute__((always_inline)) void SettingNewValuesForShortcutDetection(int isEnabled) { // Setting limits for analogue watchdog to catch overcurrent events static int16_t last_shortcut_current_in_mV = 0; if (last_shortcut_current_in_mV != sys_data.s.shortcut_current_in_mV) { HAL_ADC_Stop_DMA(&hadc1); // Compensating possible skin-effect // Shortcut current lower then 400A does not make much sense if (sys_data.s.shortcut_current_in_mV < sys_data.s.copper_v_drop) sys_data.s.shortcut_current_in_mV = sys_data.s.copper_v_drop; if (sys_data.s.shortcut_current_in_mV > ADC_VREF) sys_data.s.shortcut_current_in_mV = ADC_VREF; //int32_t maxShortCutCurrent = (CONTROL_CURRENT_A * ADC_MAX_VALUE) / sys_data.s.copper_v_drop_adc; //if (compensatedShortcut_current > maxShortCutCurrent) compensatedShortcut_current = maxShortCutCurrent; last_shortcut_current_in_mV = sys_data.s.shortcut_current_in_mV; // Refreshing shortcut detecting value for Analog Watchdog ADC_AnalogWDGConfTypeDef AnalogWDGConfig = {0}; AnalogWDGConfig.WatchdogNumber = ADC_ANALOGWATCHDOG_2; AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG; AnalogWDGConfig.Channel = ADC_CHANNEL_2; // I+ current sensor AnalogWDGConfig.ITMode = (isEnabled)? ENABLE: DISABLE; AnalogWDGConfig.HighThreshold = (sys_data.s.shortcut_current_in_mV * ADC_MAX_VALUE)/ADC_VREF; AnalogWDGConfig.LowThreshold = 0x000; if (HAL_ADC_AnalogWDGConfig(&hadc1, &AnalogWDGConfig) != HAL_OK) Error_Handler(); AnalogWDGConfig.WatchdogNumber = ADC_ANALOGWATCHDOG_3; AnalogWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG; AnalogWDGConfig.Channel = ADC_CHANNEL_6; // I- current sensor AnalogWDGConfig.ITMode = (isEnabled)? ENABLE: DISABLE;; AnalogWDGConfig.HighThreshold = (sys_data.s.shortcut_current_in_mV * ADC_MAX_VALUE)/ADC_VREF; //sys_data.s.reserved = AnalogWDGConfig.HighThreshold; AnalogWDGConfig.LowThreshold = 0x000; if (HAL_ADC_AnalogWDGConfig(&hadc1, &AnalogWDGConfig) != HAL_OK) Error_Handler(); if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_values, ADC_CHANNELS) != HAL_OK) LOG_E(TAG, "Cannot start ADC in DMA mode!"); DMA1_Channel1->CCR &= ~DMA_CCR_HTIE; // Disabling Half-Transfer interrupt, because we don't need it } } //------------------------------------------------------------------------------ #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF inline __attribute__((always_inline)) void DisableShortCutDetection(void) { __HAL_ADC_DISABLE_IT(&hadc1, ADC_IT_AWD2); __HAL_ADC_DISABLE_IT(&hadc1, ADC_IT_AWD3); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD2); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD3); // Switch-off process lasts approximately 1ms // We start this timer and in interrupt reactivate shortcut detection HAL_TIM_Base_Stop_IT(&htim15); __HAL_TIM_SetCounter(&htim15, 0); __HAL_TIM_CLEAR_FLAG(&htim15, TIM_IT_UPDATE); HAL_TIM_Base_Start_IT(&htim15); //HAL_GPIO_WritePin(TP2_GPIO_Port, TP2_Pin, GPIO_PIN_SET); } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void EnableShortCutDetection(void) { __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD2); __HAL_ADC_CLEAR_FLAG(&hadc1, ADC_FLAG_AWD3); __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_AWD2); __HAL_ADC_ENABLE_IT(&hadc1, ADC_IT_AWD3); //HAL_GPIO_WritePin(TP2_GPIO_Port, TP2_Pin, GPIO_PIN_RESET); } #endif //------------------------------------------------------------------------------ static inline __attribute__((always_inline)) void BatteryLowVoltageProtection(/*uint32_t current_time*/) { //static uint32_t last_time = 0; // During first start, somtimes we read 0s from ADC, so we need to wait for a while before calculating Min & Max values, especially Min values //if (current_time - last_time >= 0U) { //last_time = current_time; CalculatingAndAveragingVoltageOnContactB(); TestingForLowVoltageShutdown(); } } //------------------------------------------------------------------------------ void HeavyCalculations(uint32_t current_time) { static uint32_t heavy_calc_last_time = 0; static uint32_t HEAVY_CALCULATIONS_PERIOD = 3000; static int32_t temperature_accum = 0; int current_temperature; //static int32_t prev_ursense_voltage = 0; //int const ubsenseb_voltage_accum_avg = 5; //int const ubsensea_voltage_accum_avg = 4; static int64_t ubbsense_voltage_accum = 0; // During first start, somtimes we read 0s from ADC, so we need to wait for a while before calculating Min & Max values, especially Min values if (current_time - heavy_calc_last_time > HEAVY_CALCULATIONS_PERIOD) { heavy_calc_last_time = current_time; HEAVY_CALCULATIONS_PERIOD = 135; CalculatingSwitchSideVoltage(); CalculatingMinMaxVoltagesForContactA(); //CalculatingAndAveragingVoltageOnContactB(); //TestingForLowVoltageShutdown(); CalculatingMinMaxVoltagesForContactB(); // Sliding average calculation for board temperature current_temperature = (((MAX_TEMP - MIN_TEMP)*((int32_t)ADC_values[TEMP_CHANNEL] - TEMP_SENSOR_ADC_AT_MINUS30))/(TEMP_SENSOR_ADC_AT_PLUS100 - TEMP_SENSOR_ADC_AT_MINUS30)) + MIN_TEMP; temperature_accum -= sys_data.s.temperature; temperature_accum += current_temperature; sys_data.s.temperature = temperature_accum / 32;//>> 5; //sys_data.s.temperature = current_temperature; // Calculating MIN & MAX temperature values if (sys_data.s.temperature > sys_data.s.max_temperature) sys_data.s.max_temperature = sys_data.s.temperature; else if (sys_data.s.temperature < sys_data.s.min_temperature) sys_data.s.min_temperature = sys_data.s.temperature; // If temperature is above critical then we initiate temperature shutdown if (sys_data.s.temperature >= sys_data.s.temperature_shutdown) { if (temperature_shutdown_is_active == 0) { temperature_shutdown_is_active = 1; sys_data.s.device_status |= (1 << OVERTEMP_ERROR); sys_data.s.overtemp_error_cnt++; statDataChanged = 1; } } else if (sys_data.s.temperature < (((100 - TEMP_RECOVER_PERCENT)*sys_data.s.temperature_shutdown)/100)) { if (auto_recover_from_temp_shutdown_is_enabled) { temperature_shutdown_is_active = 0; sys_data.s.device_status &= ~(1 << OVERTEMP_ERROR); } } if (overcurrent_shutdown_is_active == 1) sys_data.s.device_status |= (1 << OVERCURRENT_ERROR); else sys_data.s.device_status &= ~(1 << OVERCURRENT_ERROR); if (overload_shutdown_is_active == 1) sys_data.s.device_status |= (1 << OVERLOAD_ERROR); else sys_data.s.device_status &= ~(1 << OVERLOAD_ERROR); if (mosfets_voltagedrop_shutdown_is_active == 1) sys_data.s.device_status |= (1 << ABBA_VOLTAGE_ERROR); else sys_data.s.device_status &= ~(1 << ABBA_VOLTAGE_ERROR); // Calculating MIN & MAX voltage drop if (sys_data.s.ursense_voltage < 0) { // Discharge if (-sys_data.s.ursense_voltage > sys_data.s.max_discharge_ursense_voltage) sys_data.s.max_discharge_ursense_voltage = -sys_data.s.ursense_voltage; else if (-sys_data.s.ursense_voltage < sys_data.s.min_discharge_ursense_voltage) sys_data.s.min_discharge_ursense_voltage = -sys_data.s.ursense_voltage; } else { // Charge if (sys_data.s.ursense_voltage > sys_data.s.max_charge_ursense_voltage) sys_data.s.max_charge_ursense_voltage = sys_data.s.ursense_voltage; else if (sys_data.s.ursense_voltage < sys_data.s.min_charge_ursense_voltage) sys_data.s.min_charge_ursense_voltage = sys_data.s.ursense_voltage; } // Saving raw ADC value for voltage drop in special register sys_data.s.adc_plus_current_sensor = rawContactVoltageDropPlus; sys_data.s.adc_minus_current_sensor = rawContactVoltageDropMinus; // Calculating averaged voltage drop on COPPER-plate on contact B static int32_t rawContactVoltageDropPlus_accum = 0; static int32_t rawContactVoltageDropMinus_accum = 0; static int32_t tmp_i_plus = 0; static int32_t tmp_i_minus = 0; const int32_t AVG = 16; rawContactVoltageDropPlus_accum -= tmp_i_plus; rawContactVoltageDropPlus_accum += rawContactVoltageDropPlus; tmp_i_plus = rawContactVoltageDropPlus_accum / AVG; rawContactVoltageDropMinus_accum -= tmp_i_minus; rawContactVoltageDropMinus_accum += rawContactVoltageDropMinus; tmp_i_minus = rawContactVoltageDropMinus_accum / AVG; uint32_t final_i_plus = (tmp_i_plus >= sys_data.s.i_plus_offset)? tmp_i_plus - sys_data.s.i_plus_offset: 0; uint32_t final_i_minus = (tmp_i_minus >= sys_data.s.i_minus_offset)? tmp_i_minus - sys_data.s.i_minus_offset: 0; int32_t rawContactVoltageDrop = final_i_plus - final_i_minus; sys_data.s.ubbsense_voltage = (rawContactVoltageDrop * ADC_VREF) / ADC_MAX_VALUE; sys_data.s.current = (rawContactVoltageDrop * CONTROL_CURRENT_A) / sys_data.s.copper_v_drop_adc; //SEGGER_RTT_printf(0, "Iadc+ = %u Iadc- = %u Iadc = %d Ubb = %d mV I = %d A\n", final_i_plus, final_i_minus, rawContactVoltageDrop, sys_data.s.ubbsense_voltage, sys_data.s.current); if ((sys_data.s.relay_status == RELAY_IS_OPENED) && (InternalGreenLED_Management != &TurnGreenLEDOff)) InternalGreenLED_Management = &TurnGreenLEDOff; else if ((sys_data.s.relay_status == RELAY_IS_CLOSED) && (InternalGreenLED_Management != &TurnGreenLEDOn)) InternalGreenLED_Management = &TurnGreenLEDOn; else if (InternalGreenLED_Management != &GreenLEDShortBlinking) InternalGreenLED_Management = &GreenLEDShortBlinking; GPIO_PinState current_OVP_state = HAL_GPIO_ReadPin(OVP_IN_GPIO_Port, OVP_IN_Pin); GPIO_PinState current_LVP_state = HAL_GPIO_ReadPin(LVP_IN_GPIO_Port, LVP_IN_Pin); if (LVP_OVP_logic == LOGIC_POSITIV) { //if (sys_data.s.lvp_state != 2) //{ if (current_LVP_state == GPIO_PIN_SET) sys_data.s.lvp_state = 0; else sys_data.s.lvp_state = 1; //} //if (sys_data.s.ovp_state != 2) //{ if (current_OVP_state == GPIO_PIN_SET) sys_data.s.ovp_state = 0; else sys_data.s.ovp_state = 1; //} } else { //if (sys_data.s.lvp_state != 2) //{ if (current_LVP_state == GPIO_PIN_RESET) sys_data.s.lvp_state = 0; else sys_data.s.lvp_state = 1; //} //if (sys_data.s.ovp_state != 2) //{ if (current_OVP_state == GPIO_PIN_RESET) sys_data.s.ovp_state = 0; else sys_data.s.ovp_state = 1; //} } //HAL_GPIO_TogglePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin); // If voltage on contact A is greater than voltage on contact B, then we assume a charger presence // We allow a pre-heater to be turned on // when OVP sygnal is active - when battery is cold static int32_t heater_cnt = 0; if ((sys_data.s.relay_status == RELAY_IS_CLOSED) || (sys_data.s.relay_status == ONLY_BA_OPENED) || (sys_data.s.relay_status == ONLY_AB_OPENED)) { if ((sys_data.s.ursense_voltage > BAT_CHARGE_SIGN_V)) { HAL_GPIO_WritePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin, OUT_CTRL_ACTIVATE); heater_cnt = 500; //if (sys_data.s.ovp_state == 1) HAL_GPIO_WritePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin, GPIO_PIN_SET); //else HAL_GPIO_WritePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin, GPIO_PIN_RESET); } else if (sys_data.s.ursense_voltage <= 0) { if (heater_cnt > 0) heater_cnt--; if (heater_cnt == 0) HAL_GPIO_WritePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin, OUT_CTRL_DEACTIVATE); } } else HAL_GPIO_WritePin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin, OUT_CTRL_DEACTIVATE); // Updating Inrush-Current value (A) static uint16_t last_inrush_max_current_in_mV = 0; if ((last_inrush_max_current_in_mV != sys_data.s.inrush_max_current_in_mV) && (sys_data.s.inrush_max_current_in_mV > sys_data.s.copper_v_drop)) { if (sys_data.s.inrush_max_current_in_mV >= ADC_VREF) sys_data.s.inrush_max_current_in_mV = ADC_VREF; last_inrush_max_current_in_mV = sys_data.s.inrush_max_current_in_mV; sys_data.s.inrush_max_current_in_adc = (sys_data.s.inrush_max_current_in_mV * ADC_MAX_VALUE) / ADC_VREF; maxIntegral = sys_data.s.inrush_max_current_in_adc * sys_data.s.inrush_curr_integral_steps; } // Updating Inrush-Current period value (µs) static uint16_t last_inrush_curr_period = 0; if (last_inrush_curr_period != sys_data.s.inrush_curr_period) { if (sys_data.s.inrush_curr_period < 31) sys_data.s.inrush_curr_period = 31; last_inrush_curr_period = sys_data.s.inrush_curr_period; sys_data.s.inrush_curr_integral_steps = (CURRENT_INTEGRAL_FREQ * sys_data.s.inrush_curr_period) / (1000*1000); maxIntegral = sys_data.s.inrush_max_current_in_adc * sys_data.s.inrush_curr_integral_steps; } // Testing if there is a current on battery heater if (HAL_GPIO_ReadPin(OUT_CS_GPIO_Port, OUT_CS_Pin) == GPIO_PIN_SET) sys_data.s.heater_status = 1; else sys_data.s.heater_status = 0; SettingNewValuesForShortcutDetection(1); } } //------------------------------------------------------------------------------ void DEBUG_print(uint32_t current_time) { static uint32_t debug_print_old_time = 0; if (current_time - debug_print_old_time > 77) // 77 { //SEGGER_RTT_printf(0, "%4d\n", rawVoltageDrop); debug_print_old_time = current_time; const char* separator_color = RTT_CTRL_TEXT_BLACK; const char* values_color = RTT_CTRL_TEXT_BRIGHT_GREEN; SEGGER_RTT_printf(0, "%s%s", values_color, TAG); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "Vab: %4d mV", sys_data.s.ursense_voltage); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "Vbb: %5d mV", sys_data.s.ubbsense_voltage); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "I: %5d A", sys_data.s.current); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "Va: %6d mV", sys_data.s.ubsensea_voltage); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "Vb: %6d mV", sys_data.s.ubsenseb_voltage); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "OVP: %1s", (sys_data.s.ovp_state == 0)? "N": RTT_CTRL_TEXT_RED"Y"RTT_CTRL_TEXT_BRIGHT_GREEN); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "LVP: %1s", (sys_data.s.lvp_state == 0)? "N": RTT_CTRL_TEXT_RED"Y"RTT_CTRL_TEXT_BRIGHT_GREEN); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "DAC_A: %4d", DAC_HANDLE.Instance->DAC_CH_A); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "DAC_B: %4d", DAC_HANDLE.Instance->DAC_CH_B); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "R: %s", (sys_data.s.relay_status == 0)? "OP": (sys_data.s.relay_status == 1)? "CL": (sys_data.s.relay_status == 2)? "BA": "AB"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); SEGGER_RTT_printf(0, "T: %2d.%d \260C", sys_data.s.temperature/10, abs(sys_data.s.temperature - (sys_data.s.temperature/10)*10) ); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (sys_data.s.device_status & (1 << OVERTEMP_ERROR)) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_RED); SEGGER_RTT_printf(0, "%s", "OT"); } else SEGGER_RTT_printf(0, "%s", "OT"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (sys_data.s.device_status & (1 << OVERCURRENT_ERROR)) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_RED); SEGGER_RTT_printf(0, "%s", "OC"); } else SEGGER_RTT_printf(0, "%s", "OC"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (sys_data.s.device_status & (1 << OVERLOAD_ERROR)) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_RED); SEGGER_RTT_printf(0, "%s", "OL"); } else SEGGER_RTT_printf(0, "%s", "OL"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (sys_data.s.device_status & (1 << LOWBAT_ERROR)) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_RED); SEGGER_RTT_printf(0, "%s", "LB"); } else SEGGER_RTT_printf(0, "%s", "LB"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (sys_data.s.device_status & (1 << ABBA_VOLTAGE_ERROR)) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_RED); SEGGER_RTT_printf(0, "%s", "AB"); } else SEGGER_RTT_printf(0, "%s", "AB"); SEGGER_RTT_printf(0, "%s | %s", separator_color, values_color); if (HAL_GPIO_ReadPin(OUT_CTRL_GPIO_Port, OUT_CTRL_Pin) == OUT_CTRL_ACTIVATE) { SEGGER_RTT_printf(0, "%s", RTT_CTRL_TEXT_BRIGHT_CYAN); SEGGER_RTT_printf(0, "%s", "CHG - 1"); } else { SEGGER_RTT_printf(0, "%s", "CHG - 0"); } SEGGER_RTT_printf(0, "\n"); } } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void MODBUS_Management(void) { // Modbus Kommunikation if (mbGetFrameComplete(&modbusData)) { if (mbSlaveCheckModbusRtuQuery(&modbusData) == RESPOND_TO_QUERY) { if (RS485ActiveMode) { mbSlaveProcessRtuQuery(&modbusData); } } else huart1.RxState = HAL_UART_STATE_BUSY_RX; } // This code prevents unauthorized write-access to registers // Prüfe KEY if (sys_data.s.lockKey == savedLockKey) { sys_data.s.keyAccepted = 1; keyAccepted = 1; } else { sys_data.s.keyAccepted = 0; keyAccepted = 0; } // Checking whether command parser is active or not if (command_parser_is_enabled) { // Checking command which came via Modbus if ((sys_data.s.command != 0) && (modbusData.current_query == MB_QUERY_NOTHING)) { switch (sys_data.s.command) { case COMMAND_SAVE_CONFIG: // Saving new settings without SN in FLASH if (FEEPROM_storeConfig(&sys_data, 0)) LOG_E(TAG, "Cannot save data in FLASH memory!"); // Fetching configuration from FLASH if (FEEPROM_readConfig(&sys_data)) LOG_E(TAG, "Cannot read configuration from FLASH memory!"); //if (FEEPROM_ReadLogData(&sys_data)) LOG_E(TAG, "Cannot read statistcal data from FLASH memory!"); break; case COMMAND_SAVE_LOCK_KEY: LOG_I(TAG, "SAVE LOCK-KEY COMMAND."); mb_save_lock_key(); break; case COMMAND_RESTART_MODBUS: // Modbus Re-initialization if (sys_data.s.parity_mode == 'e') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_EVEN, &huart1, accessModeTable, &keyAccepted); else if (sys_data.s.parity_mode == 'o') mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_ODD, &huart1, accessModeTable, &keyAccepted); else mbInit(&modbusData, sys_data.s.baudrate, MODBUS_UART_PARITY_NONE, &huart1, accessModeTable, &keyAccepted); break; case COMMAND_RESTORE_DEFAULTS: if (FEEPROM_fullRestore(/*&sys_data*/)) LOG_E(TAG, "Cannot restore default settings from FLASH memory!"); FEEPROM_ResetLogData(); // Fetching configuration from FLASH if (FEEPROM_readConfig(&sys_data)) LOG_E(TAG, "Cannot read configuration from FLASH memory!"); if (FEEPROM_ReadLogData(&sys_data)) LOG_E(TAG, "Cannot read statistcal data from FLASH memory!"); break; case COMMAND_ON_SWITCH: StartOnMode(); break; case COMMAND_OFF_SWITCH: StartOffMode(0); break; case COMMAND_AUTO_SWITCH: StartAutoMode(); break; case COMMAND_SAVE_CONFIG_WITH_SN: // Saving new settings with new SN in FLASH if (FEEPROM_storeConfig(&sys_data, 1)) LOG_E(TAG, "Cannot save new SN in FLASH memory!"); // Fetching configuration from FLASH if (FEEPROM_readConfig(&sys_data)) LOG_E(TAG, "Cannot read configuration from FLASH memory!"); //if (FEEPROM_ReadLogData(&sys_data)) LOG_E(TAG, "Cannot read statistcal data from FLASH memory!"); break; case COMMAND_RESTART: OpenBothMOSFETSVeryFast(); NVIC_SystemReset(); break; case COMMAND_OFFSET_CALIBRATION: if (Callibration == DoNothing) Callibration = &CallibrateVoltageDropABMiddlePointOffset; break; case COMMAND_CURRENT_CALIBRATION: if (Callibration == DoNothing) Callibration = &CallibrateControlCurrentVoltageDropOnContactBB; break; case COMMAND_CURRENT_OFFSET_CALIBRATION: if (Callibration == DoNothing) Callibration = &CallibrateCurrentSensorZeroOffsetOnContactBB; break; case COMMAND_TURN_OVERLOAD_DETECTION_OFF: InrushCurrentManagement = &DoNothing; break; case COMMAND_TURN_OVERLOAD_DETECTION_ON: InrushCurrentManagement = &InrushCurrentDetected; break; default: // When we recieve some unknown command we freeze our command parser for 10 sec (to prevent bruteforce). __HAL_TIM_CLEAR_FLAG(&htim17, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&htim17, 0); if (HAL_TIM_Base_Start_IT(&htim17) != HAL_OK) LOG_E(TAG, "Cannot start TIM17 in ISR mode!"); command_parser_is_enabled = 0; LOG_W(TAG, "Unknown command!"); } sys_data.s.command = 0; } } else sys_data.s.command = 0; } //------------------------------------------------------------------------------ void OVP_not_present__LVP_ignored(void) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); #ifdef INVERTER_CAP_PRECHARGE SetReturnFunction(&ADC_OVP_not_present__LVP_ignored); MOSFETS_Management = &PreChargeStage; #else MOSFETS_Management = &ADC_OVP_not_present__LVP_ignored; #endif sys_data.s.relay_status = RELAY_IS_CLOSED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; } //------------------------------------------------------------------------------ void OVP_present__LVP_ignored(void) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_OVP_present__LVP_ignored; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; sys_data.s.ovp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_present__LVP_ignored_NoAutoreconnect(void) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_Open_Both_MOSFETs; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); sys_data.s.user_button_mode = SWITCH_OFF; ExternalGreenLED_Management = &TurnExternalGreenLEDOff; // After 100ms we turn off VBOOST voltage for power saving __HAL_TIM_CLEAR_FLAG(&VBOOST_OFF_TIMER, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&VBOOST_OFF_TIMER, 0); HAL_TIM_Base_Start_IT(&VBOOST_OFF_TIMER); sys_data.s.ovp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_ignored__LVP_not_present(void) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); #ifdef INVERTER_CAP_PRECHARGE SetReturnFunction(&ADC_OVP_ignored__LVP_not_present); MOSFETS_Management = &PreChargeStage; #else MOSFETS_Management = &ADC_OVP_ignored__LVP_not_present; #endif sys_data.s.relay_status = RELAY_IS_CLOSED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; //ExternalRedLED_Management = &TurnExternalRedLEDOff; } //------------------------------------------------------------------------------ void OVP_ignored__LVP_present(void) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_OVP_ignored__LVP_present; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; //ExternalRedLED_Management = &ExternalRedLEDShortBlinking; sys_data.s.lvp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_ignored__LVP_present_NoAutoreconnect(void) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_Open_Both_MOSFETs; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); sys_data.s.user_button_mode = SWITCH_OFF; ExternalGreenLED_Management = &TurnExternalGreenLEDOff; // After 100ms we turn off VBOOST voltage for power saving __HAL_TIM_CLEAR_FLAG(&VBOOST_OFF_TIMER, TIM_FLAG_UPDATE); __HAL_TIM_SET_COUNTER(&VBOOST_OFF_TIMER, 0); HAL_TIM_Base_Start_IT(&VBOOST_OFF_TIMER); sys_data.s.lvp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_not_present__LVP_not_present(void) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); #ifdef INVERTER_CAP_PRECHARGE SetReturnFunction(&ADC_OVP_not_present__LVP_not_present); MOSFETS_Management = &PreChargeStage; #else MOSFETS_Management = &ADC_OVP_not_present__LVP_not_present; #endif HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); sys_data.s.relay_status = RELAY_IS_CLOSED; ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; TurnExternalRedLEDOff(); } //------------------------------------------------------------------------------ void OVP_not_present__LVP_present(void) { //#ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF // DisableShortCutDetection(); //#endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_OVP_not_present__LVP_present; sys_data.s.relay_status = ONLY_AB_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; TurnExternalRedLEDOff(); sys_data.s.lvp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_present__LVP_not_present(void) { HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_OVP_present__LVP_not_present; sys_data.s.relay_status = ONLY_BA_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &ExternalGreenLEDShortBlinking; TurnExternalRedLEDOff(); //GreenLED_Management = sys_data.s.ovp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ void OVP_present__LVP_present(void) { #ifdef DISABLE_SHORTCUT_DETECTION_DURING_SWITCH_OFF DisableShortCutDetection(); #endif HAL_NVIC_DisableIRQ(ADC_DMA_IRQ); MOSFETS_Management = &ADC_OVP_present__LVP_present; sys_data.s.relay_status = RELAY_IS_OPENED; HAL_NVIC_EnableIRQ(ADC_DMA_IRQ); ExternalGreenLED_Management = &TurnExternalGreenLEDOff; ExternalRedLED_Management = &ExternalRedLED2ShortOnThen2LongOnThenLongPauseBlinking; sys_data.s.lvp_cnt++; sys_data.s.ovp_cnt++; statDataChanged = 1; } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_present__LVP_ignored(void) #else void ADC_OVP_present__LVP_ignored(void) #endif { OpenBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_ignored__LVP_present(void) #else void ADC_OVP_ignored__LVP_present(void) #endif { OpenBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_Open_Both_MOSFETs(void) #else void ADC_Open_Both_MOSFETs(void) #endif { OpenBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_present__LVP_present(void) #else void ADC_OVP_present__LVP_present(void) #endif { OpenBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_not_present__LVP_present(void) #else void ADC_OVP_not_present__LVP_present(void) #endif { // Reading current DAC value for MOSFET B uint32_t dacA_value = DAC_HANDLE.Instance->DAC_CH_A; // Checking voltage drop on MOSFETs if ((rawMOSFETsVoltageDrop + sys_data.s.ab_middle_point_offset) < POS_ALLOWED_MOSFETS_V_DROP_ADC) { if (dacA_value >= (DAC_0V + DAC_STEP)) dacA_value -= DAC_STEP; } else if ((rawMOSFETsVoltageDrop + sys_data.s.ab_middle_point_offset) > POS_ALLOWED_MOSFETS_V_DROP_ADC) { if (dacA_value <= (DAC_3V - DAC_STEP)) dacA_value += DAC_STEP; } // Writing DAC value for MOSFET B *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = dacA_value; // Reading DAC value for MOSFET channel A uint32_t dacB_value = DAC_HANDLE.Instance->DAC_CH_B; // We do not allow DAC value to be greater than max value if (dacB_value <= (DAC_3V - DAC_STEP)) dacB_value += DAC_STEP; // Sending new DAC value to DAC *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = dacB_value; } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_present__LVP_not_present(void) #else void ADC_OVP_present__LVP_not_present(void) #endif { // Reading current DAC value for MOSFET B channel uint32_t dacB_value = DAC_HANDLE.Instance->DAC_CH_B; // Making sure that BA voltage drop during discharging is within the limit if ((rawMOSFETsVoltageDrop + sys_data.s.ab_middle_point_offset) < NEG_ALLOWED_MOSFETS_V_DROP_ADC) { if (dacB_value <= (DAC_3V - DAC_STEP)) dacB_value += DAC_STEP; } else if ((rawMOSFETsVoltageDrop + sys_data.s.ab_middle_point_offset) > NEG_ALLOWED_MOSFETS_V_DROP_ADC) { if (dacB_value >= (DAC_0V + DAC_STEP)) dacB_value -= DAC_STEP; } // Writing DAC value for MOSFET A channel *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = dacB_value; // Reading DAC value for MOSFET channel A uint32_t dacA_value = DAC_HANDLE.Instance->DAC_CH_A; // Channel A must be fully closed, so we increase smoothly DAC value // We do not allow DAC value to be greater than max value if (dacA_value <= (DAC_3V - DAC_STEP)) dacA_value += DAC_STEP; // Sending new DAC value to DAC *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = dacA_value; } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_not_present__LVP_ignored(void) #else void ADC_OVP_not_present__LVP_ignored(void) #endif { CloseBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_ignored__LVP_not_present(void) #else void ADC_OVP_ignored__LVP_not_present(void) #endif { CloseBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_Close_Both_MOSFETs(void) #else void ADC_Close_Both_MOSFETs(void) #endif { CloseBothMOSFETS(); } //------------------------------------------------------------------------------ #ifdef USE_RAM_FUNC __RAM_FUNC void ADC_OVP_not_present__LVP_not_present(void) #else void ADC_OVP_not_present__LVP_not_present(void) #endif { CloseBothMOSFETS(); } //----------------------------------------------------------------------------- void CallibrateCurrentSensorZeroOffsetOnContactBB(void) { // Assuming that load is not connected uint32_t adc_value_accum_i_plus_channel = 0; uint32_t adc_value_accum_i_minus_channel = 0; const uint32_t SAMPLE_COUNT = 50000; // Sampling N values for (int i = 0; i < SAMPLE_COUNT; i++) { // Calculating offsets on both current sensors adc_value_accum_i_plus_channel += rawContactVoltageDropPlus; adc_value_accum_i_minus_channel += rawContactVoltageDropMinus; SEGGER_RTT_printf(0, "\t[%4d] Sampled values: I+ = %6u I- = %6u\n", i, rawContactVoltageDropPlus, rawContactVoltageDropMinus); //SEGGER_RTT_printf(0, "%u,%u\n", ADC_values[I_PLUS_CHANNEL], ADC_values[I_MINUS_CHANNEL]); } sys_data.s.i_plus_offset = adc_value_accum_i_plus_channel / SAMPLE_COUNT; sys_data.s.i_minus_offset = adc_value_accum_i_minus_channel / SAMPLE_COUNT; SEGGER_RTT_printf(0, "\t\tOffset values: I+ = %u I- = %u\n", sys_data.s.i_plus_offset, sys_data.s.i_minus_offset); Callibration = &DoNothing; } //------------------------------------------------------------------------------ int32_t Rescale(int32_t x, int32_t x1, int32_t x2, int32_t y1, int32_t y2) { int32_t res = ((y2 - y1) * (x - x1)) / (x2 - x1) + y1; return res; } //------------------------------------------------------------------------------ int32_t TemperatureCompensation(int32_t rawADCValueUnderMaxCurrent, int32_t temp_dGrad) { if (temp_dGrad < 200) rawADCValueUnderMaxCurrent = (rawADCValueUnderMaxCurrent * (100 + 10)) / 100; // +10% else if (temp_dGrad >= 200 && temp_dGrad < 600) rawADCValueUnderMaxCurrent = (rawADCValueUnderMaxCurrent * (100 + Rescale(temp_dGrad, 200, 600, 10, 0))) / 100; // +[0%...10%] else if (temp_dGrad >= 600) rawADCValueUnderMaxCurrent = rawADCValueUnderMaxCurrent; // +0% return rawADCValueUnderMaxCurrent; } //----------------------------------------------------------------------------- void CallibrateControlCurrentVoltageDropOnContactBB(void) { // Assuming that 500A load is connected LOG_I(TAG, "Current callibration sequence started."); // Turning off inrush current protection //InrushCurrentManagement = &DoNothing; uint32_t start_time = HAL_GetTick(); int32_t ubbsense_adc_accum = 0; int32_t ubbsense_adc = 0; int32_t ubbsense_voltage = 0; while (HAL_GetTick() - start_time < 60000) { ubbsense_adc_accum -= ubbsense_adc; ubbsense_adc_accum += rawContactVoltageDropMinus; ubbsense_adc = ubbsense_adc_accum / 16; SEGGER_RTT_printf(0, "\t\tVoltage-drop ADC value: %5d.\n", ubbsense_adc); HAL_Delay(1); } ubbsense_adc -= sys_data.s.i_minus_offset; ubbsense_adc = TemperatureCompensation(ubbsense_adc, sys_data.s.temperature); ubbsense_voltage = (ubbsense_adc * ADC_VREF) / ADC_MAX_VALUE; // Recording copper-plate voltage-drop under control current sys_data.s.copper_v_drop = ubbsense_voltage; // Recording ADC value drop under control current sys_data.s.copper_v_drop_adc = ubbsense_adc; sys_data.s.copper_v_drop_adc_limit = (sys_data.s.copper_v_drop_adc * 110) / 100; SEGGER_RTT_printf(0, "\t\t\tFinal voltage-drop ADC value: %4u. Final voltage-drop value: %3u mV\n", sys_data.s.copper_v_drop_adc, sys_data.s.copper_v_drop); // Updating inrush current settings //sys_data.s.inrush_max_current_in_adc = (sys_data.s.inrush_max_current * sys_data.s.copper_v_drop_adc) / CONTROL_CURRENT_A; //maxIntegral = sys_data.s.inrush_max_current_in_adc * sys_data.s.inrush_curr_integral_steps; Callibration = &DoNothing; //InrushCurrentManagement = &InrushCurrentDetected; // Test program disables this, so we must re-enable it after callibration } //----------------------------------------------------------------------------- void CallibrateVoltageDropABMiddlePointOffset(void) { // Assuming that load is not connected uint32_t adc_value_accum = 0; const uint32_t SAMPLE_COUNT = 50000; // Sampling N values for (int i = 0; i < SAMPLE_COUNT; i++) { // Calculating averaged value for real voltage drop between contacts B and A in ADC values adc_value_accum += rawMOSFETsVoltageDrop; SEGGER_RTT_printf(0, "\t[%4d] Sampled value: %4d\n", i, rawMOSFETsVoltageDrop); } sys_data.s.ab_middle_point_offset = (ADC_MAX_VALUE>>1) - (adc_value_accum / SAMPLE_COUNT); SEGGER_RTT_printf(0, "\t\tOffset value: %4d\n", sys_data.s.ab_middle_point_offset); Callibration = &DoNothing; } //------------------------------------------------------------------------------ void InrushCurrentDetected(void) { OpenBothMOSFETSVeryFast(); MOSFETS_Management = &DoNothing; if (overcurrent_shutdown_is_active == 0 ) { if (overload_shutdown_is_active == 0) { sys_data.s.overload_error_cnt++; statDataChanged = 1; } overload_shutdown_is_active = 1; overload_shutdown_time = HAL_GetTick(); } } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void OpenBothMOSFETS(void) { // Reading DAC values for MOSFET channels A and B uint32_t dacA_value = DAC_HANDLE.Instance->DAC_CH_A; uint32_t dacB_value = DAC_HANDLE.Instance->DAC_CH_B; // Opening MOSFETS if (dacA_value >= (DAC_0V + DAC_STEP)) dacA_value -= DAC_STEP; if (dacB_value >= (DAC_0V + DAC_STEP)) dacB_value -= DAC_STEP; // Setting new values *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = dacA_value; *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = dacB_value; } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void CloseBothMOSFETS(void) { // Reading DAC values for MOSFET channels A and B uint32_t dacA_value = DAC_HANDLE.Instance->DAC_CH_A; uint32_t dacB_value = DAC_HANDLE.Instance->DAC_CH_B; // Closing MOSFETS if (dacA_value <= (DAC_3V - DAC_STEP)) dacA_value += DAC_STEP; if (dacB_value <= (DAC_3V - DAC_STEP)) dacB_value += DAC_STEP; // Setting new values *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = dacA_value; *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = dacB_value; } //------------------------------------------------------------------------------ void CloseBothMOSFETSVeryFast(void) { *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = DAC_3V; // Expression from HAL_DAC_SetValue function *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = DAC_3V; // Expression from HAL_DAC_SetValue function sys_data.s.relay_status = RELAY_IS_CLOSED; } //------------------------------------------------------------------------------ inline __attribute__((always_inline)) void OpenBothMOSFETSVeryFast(void) { *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_A_ALIGNMENT(DAC_ALIGN_12B_R)) = DAC_0V; // Expression from HAL_DAC_SetValue function *(__IO uint32_t *)((uint32_t)DAC_HANDLE.Instance + DAC_CH_B_ALIGNMENT(DAC_ALIGN_12B_R)) = DAC_0V; // Expression from HAL_DAC_SetValue function sys_data.s.relay_status = RELAY_IS_OPENED; } //------------------------------------------------------------------------------ void ShowSlaveAddressOnLED(uint16_t address, GPIO_TypeDef *port, uint16_t pin) { for (int i = 0; i < address; i++) { HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); HAL_Delay(333); HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); HAL_Delay(333); } } //------------------------------------------------------------------------------ void StartUpSequence(void) { HAL_GPIO_WritePin(LED_FUNCTION_GPIO_Port, LED_FUNCTION_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_ERROR_GPIO_Port, LED_ERROR_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_SET); HAL_Delay(1000); HAL_GPIO_WritePin(LED_FUNCTION_GPIO_Port, LED_FUNCTION_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_ERROR_GPIO_Port, LED_ERROR_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_STATE_GPIO_Port, LED_STATE_Pin, GPIO_PIN_RESET); HAL_Delay(500); } //----------------------------------------------------------------------------- #ifdef USE_RAM_FUNC __RAM_FUNC void DoNothing(void) #else void DoNothing(void) #endif { } //----------------------------------------------------------------------------- /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ LOG_E(TAG, "HAL error!"); /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ SEGGER_RTT_printf(0, "Wrong parameters value: file %s on line %d\n", file, line); /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */