From dd4ff43515c078e3f75ad8014f06bd4a406363a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Tue, 12 Mar 2024 21:27:25 +0200 Subject: [PATCH 01/35] Prepared for Cpp version --- firmware/src/hw/analog.c | 45 - firmware/src/hw/analog.h | 20 - firmware/src/hw/board/ain.c | 77 - firmware/src/hw/board/ain.h | 26 - firmware/src/hw/board/config.h | 32 - firmware/src/hw/board/din.c | 83 - firmware/src/hw/board/din.h | 37 - firmware/src/hw/board/dout.c | 58 - firmware/src/hw/board/dout.h | 34 - firmware/src/hw/board/halfbridge.c | 179 -- firmware/src/hw/board/halfbridge.h | 34 - firmware/src/hw/board/mcu/mcu_hal.h | 120 - firmware/src/hw/board/mcu/mcu_hal_r8.c | 418 --- firmware/src/hw/board/odout.c | 80 - firmware/src/hw/board/odout.h | 37 - firmware/src/hw/board/setup.c | 23 - firmware/src/hw/board/setup.h | 14 - firmware/src/hw/board/utils/faults.c | 67 - firmware/src/hw/board/utils/faults.h | 37 - firmware/src/hw/board/utils/fuses.c | 81 - firmware/src/hw/board/utils/fuses.h | 36 - firmware/src/hw/board/utils/utils.c | 272 -- firmware/src/hw/board/utils/utils.h | 37 - firmware/src/hw/buttons.c | 173 -- firmware/src/hw/buttons.h | 54 - firmware/src/hw/config.h | 45 - firmware/src/hw/hb_control.c | 399 --- firmware/src/hw/hb_control.h | 67 - firmware/src/hw/led_display.c | 38 - firmware/src/hw/led_display.h | 19 - firmware/src/hw/mcu/mcu_hal.h | 102 + firmware/src/hw/mcu/mcu_r8_hal.cpp | 463 +++ firmware/src/hw/startup.c | 16 - firmware/src/hw/startup.h | 14 - firmware/src/logic/coil.c | 41 - firmware/src/logic/coil.h | 17 - firmware/src/logic/display.c | 108 - firmware/src/logic/display.h | 37 - firmware/src/logic/force.c | 63 - firmware/src/logic/force.h | 23 - firmware/src/logic/pot.c | 42 - firmware/src/logic/pot.h | 19 - firmware/src/logic/user_force.c | 52 - firmware/src/logic/user_force.h | 18 - firmware/src/main.c | 127 - firmware/src/main.cpp | 18 + .../{uDCCD_Controller.atsln => uDCCD.atsln} | 2 +- ...ponentinfo.xml => uDCCD.componentinfo.xml} | 4 +- firmware/src/uDCCD.cppproj | 161 + firmware/src/uDCCD_Controller.cproj | 276 -- firmware/tests/Units_Tests.workspace | 8 - firmware/tests/Units_Tests.workspace.layout | 6 - firmware/tests/bsp_main.c | 147 - firmware/tests/hw_main.c | 102 - firmware/tests/logic_main.c | 98 - firmware/tests/mock_board/mock_board_ain.c | 32 - firmware/tests/mock_board/mock_board_ain.h | 14 - firmware/tests/mock_board/mock_board_din.c | 32 - firmware/tests/mock_board/mock_board_din.h | 14 - firmware/tests/mock_board/mock_board_dout.c | 31 - firmware/tests/mock_board/mock_board_dout.h | 14 - .../tests/mock_board/mock_board_halfbridge.c | 76 - .../tests/mock_board/mock_board_halfbridge.h | 18 - firmware/tests/mock_board/mock_board_odout.c | 47 - firmware/tests/mock_board/mock_board_odout.h | 16 - firmware/tests/mock_board/mock_board_setup.c | 18 - firmware/tests/mock_board/mock_board_setup.h | 12 - firmware/tests/mock_mcu/mock_mcu_hal_r8.c | 178 -- firmware/tests/mock_mcu/mock_mcu_hal_r8.h | 28 - firmware/tests/uDCCD_Unit_Tests_BSP.cbp | 106 - firmware/tests/uDCCD_Unit_Tests_BSP.depend | 348 --- firmware/tests/uDCCD_Unit_Tests_BSP.layout | 90 - firmware/tests/uDCCD_Unit_Tests_HW.cbp | 105 - firmware/tests/uDCCD_Unit_Tests_HW.depend | 378 --- firmware/tests/uDCCD_Unit_Tests_HW.layout | 145 - firmware/tests/uDCCD_Unit_Tests_Logic.cbp | 71 - firmware/tests/uDCCD_Unit_Tests_Logic.depend | 80 - firmware/tests/uDCCD_Unit_Tests_Logic.layout | 70 - firmware/tests/ut_board/ut_ain.c | 96 - firmware/tests/ut_board/ut_ain.h | 9 - firmware/tests/ut_board/ut_din.c | 191 -- firmware/tests/ut_board/ut_din.h | 9 - firmware/tests/ut_board/ut_dout.c | 142 - firmware/tests/ut_board/ut_dout.h | 9 - firmware/tests/ut_board/ut_halfbridge.c | 273 -- firmware/tests/ut_board/ut_halfbridge.h | 11 - firmware/tests/ut_board/ut_odout.c | 212 -- firmware/tests/ut_board/ut_odout.h | 10 - firmware/tests/ut_board/ut_setup.c | 62 - firmware/tests/ut_board/ut_setup.h | 9 - firmware/tests/ut_hw/ut_analog.c | 73 - firmware/tests/ut_hw/ut_analog.h | 9 - firmware/tests/ut_hw/ut_buttons.c | 854 ------ firmware/tests/ut_hw/ut_buttons.h | 12 - firmware/tests/ut_hw/ut_hb_control.c | 2638 ----------------- firmware/tests/ut_hw/ut_hb_control.h | 15 - firmware/tests/ut_hw/ut_led_display.c | 170 -- firmware/tests/ut_hw/ut_led_display.h | 10 - firmware/tests/ut_hw/ut_startup.c | 48 - firmware/tests/ut_hw/ut_startup.h | 9 - firmware/tests/ut_logic/ut_coil.c | 86 - firmware/tests/ut_logic/ut_coil.h | 9 - firmware/tests/ut_logic/ut_display.c | 552 ---- firmware/tests/ut_logic/ut_display.h | 19 - firmware/tests/ut_logic/ut_force.c | 170 -- firmware/tests/ut_logic/ut_force.h | 10 - firmware/tests/ut_logic/ut_pot.c | 100 - firmware/tests/ut_logic/ut_pot.h | 9 - firmware/tests/ut_logic/ut_user_force.c | 419 --- firmware/tests/ut_logic/ut_user_force.h | 10 - firmware/tests/ut_utils/ut_fault.c | 951 ------ firmware/tests/ut_utils/ut_fault.h | 12 - firmware/tests/ut_utils/ut_fuses.c | 79 - firmware/tests/ut_utils/ut_fuses.h | 9 - firmware/tests/ut_utils/ut_utils.c | 1334 --------- firmware/tests/ut_utils/ut_utils.h | 27 - 116 files changed, 747 insertions(+), 14449 deletions(-) delete mode 100644 firmware/src/hw/analog.c delete mode 100644 firmware/src/hw/analog.h delete mode 100644 firmware/src/hw/board/ain.c delete mode 100644 firmware/src/hw/board/ain.h delete mode 100644 firmware/src/hw/board/config.h delete mode 100644 firmware/src/hw/board/din.c delete mode 100644 firmware/src/hw/board/din.h delete mode 100644 firmware/src/hw/board/dout.c delete mode 100644 firmware/src/hw/board/dout.h delete mode 100644 firmware/src/hw/board/halfbridge.c delete mode 100644 firmware/src/hw/board/halfbridge.h delete mode 100644 firmware/src/hw/board/mcu/mcu_hal.h delete mode 100644 firmware/src/hw/board/mcu/mcu_hal_r8.c delete mode 100644 firmware/src/hw/board/odout.c delete mode 100644 firmware/src/hw/board/odout.h delete mode 100644 firmware/src/hw/board/setup.c delete mode 100644 firmware/src/hw/board/setup.h delete mode 100644 firmware/src/hw/board/utils/faults.c delete mode 100644 firmware/src/hw/board/utils/faults.h delete mode 100644 firmware/src/hw/board/utils/fuses.c delete mode 100644 firmware/src/hw/board/utils/fuses.h delete mode 100644 firmware/src/hw/board/utils/utils.c delete mode 100644 firmware/src/hw/board/utils/utils.h delete mode 100644 firmware/src/hw/buttons.c delete mode 100644 firmware/src/hw/buttons.h delete mode 100644 firmware/src/hw/config.h delete mode 100644 firmware/src/hw/hb_control.c delete mode 100644 firmware/src/hw/hb_control.h delete mode 100644 firmware/src/hw/led_display.c delete mode 100644 firmware/src/hw/led_display.h create mode 100644 firmware/src/hw/mcu/mcu_hal.h create mode 100644 firmware/src/hw/mcu/mcu_r8_hal.cpp delete mode 100644 firmware/src/hw/startup.c delete mode 100644 firmware/src/hw/startup.h delete mode 100644 firmware/src/logic/coil.c delete mode 100644 firmware/src/logic/coil.h delete mode 100644 firmware/src/logic/display.c delete mode 100644 firmware/src/logic/display.h delete mode 100644 firmware/src/logic/force.c delete mode 100644 firmware/src/logic/force.h delete mode 100644 firmware/src/logic/pot.c delete mode 100644 firmware/src/logic/pot.h delete mode 100644 firmware/src/logic/user_force.c delete mode 100644 firmware/src/logic/user_force.h delete mode 100644 firmware/src/main.c create mode 100644 firmware/src/main.cpp rename firmware/src/{uDCCD_Controller.atsln => uDCCD.atsln} (85%) rename firmware/src/{uDCCD_Controller.componentinfo.xml => uDCCD.componentinfo.xml} (96%) create mode 100644 firmware/src/uDCCD.cppproj delete mode 100644 firmware/src/uDCCD_Controller.cproj delete mode 100644 firmware/tests/Units_Tests.workspace delete mode 100644 firmware/tests/Units_Tests.workspace.layout delete mode 100644 firmware/tests/bsp_main.c delete mode 100644 firmware/tests/hw_main.c delete mode 100644 firmware/tests/logic_main.c delete mode 100644 firmware/tests/mock_board/mock_board_ain.c delete mode 100644 firmware/tests/mock_board/mock_board_ain.h delete mode 100644 firmware/tests/mock_board/mock_board_din.c delete mode 100644 firmware/tests/mock_board/mock_board_din.h delete mode 100644 firmware/tests/mock_board/mock_board_dout.c delete mode 100644 firmware/tests/mock_board/mock_board_dout.h delete mode 100644 firmware/tests/mock_board/mock_board_halfbridge.c delete mode 100644 firmware/tests/mock_board/mock_board_halfbridge.h delete mode 100644 firmware/tests/mock_board/mock_board_odout.c delete mode 100644 firmware/tests/mock_board/mock_board_odout.h delete mode 100644 firmware/tests/mock_board/mock_board_setup.c delete mode 100644 firmware/tests/mock_board/mock_board_setup.h delete mode 100644 firmware/tests/mock_mcu/mock_mcu_hal_r8.c delete mode 100644 firmware/tests/mock_mcu/mock_mcu_hal_r8.h delete mode 100644 firmware/tests/uDCCD_Unit_Tests_BSP.cbp delete mode 100644 firmware/tests/uDCCD_Unit_Tests_BSP.depend delete mode 100644 firmware/tests/uDCCD_Unit_Tests_BSP.layout delete mode 100644 firmware/tests/uDCCD_Unit_Tests_HW.cbp delete mode 100644 firmware/tests/uDCCD_Unit_Tests_HW.depend delete mode 100644 firmware/tests/uDCCD_Unit_Tests_HW.layout delete mode 100644 firmware/tests/uDCCD_Unit_Tests_Logic.cbp delete mode 100644 firmware/tests/uDCCD_Unit_Tests_Logic.depend delete mode 100644 firmware/tests/uDCCD_Unit_Tests_Logic.layout delete mode 100644 firmware/tests/ut_board/ut_ain.c delete mode 100644 firmware/tests/ut_board/ut_ain.h delete mode 100644 firmware/tests/ut_board/ut_din.c delete mode 100644 firmware/tests/ut_board/ut_din.h delete mode 100644 firmware/tests/ut_board/ut_dout.c delete mode 100644 firmware/tests/ut_board/ut_dout.h delete mode 100644 firmware/tests/ut_board/ut_halfbridge.c delete mode 100644 firmware/tests/ut_board/ut_halfbridge.h delete mode 100644 firmware/tests/ut_board/ut_odout.c delete mode 100644 firmware/tests/ut_board/ut_odout.h delete mode 100644 firmware/tests/ut_board/ut_setup.c delete mode 100644 firmware/tests/ut_board/ut_setup.h delete mode 100644 firmware/tests/ut_hw/ut_analog.c delete mode 100644 firmware/tests/ut_hw/ut_analog.h delete mode 100644 firmware/tests/ut_hw/ut_buttons.c delete mode 100644 firmware/tests/ut_hw/ut_buttons.h delete mode 100644 firmware/tests/ut_hw/ut_hb_control.c delete mode 100644 firmware/tests/ut_hw/ut_hb_control.h delete mode 100644 firmware/tests/ut_hw/ut_led_display.c delete mode 100644 firmware/tests/ut_hw/ut_led_display.h delete mode 100644 firmware/tests/ut_hw/ut_startup.c delete mode 100644 firmware/tests/ut_hw/ut_startup.h delete mode 100644 firmware/tests/ut_logic/ut_coil.c delete mode 100644 firmware/tests/ut_logic/ut_coil.h delete mode 100644 firmware/tests/ut_logic/ut_display.c delete mode 100644 firmware/tests/ut_logic/ut_display.h delete mode 100644 firmware/tests/ut_logic/ut_force.c delete mode 100644 firmware/tests/ut_logic/ut_force.h delete mode 100644 firmware/tests/ut_logic/ut_pot.c delete mode 100644 firmware/tests/ut_logic/ut_pot.h delete mode 100644 firmware/tests/ut_logic/ut_user_force.c delete mode 100644 firmware/tests/ut_logic/ut_user_force.h delete mode 100644 firmware/tests/ut_utils/ut_fault.c delete mode 100644 firmware/tests/ut_utils/ut_fault.h delete mode 100644 firmware/tests/ut_utils/ut_fuses.c delete mode 100644 firmware/tests/ut_utils/ut_fuses.h delete mode 100644 firmware/tests/ut_utils/ut_utils.c delete mode 100644 firmware/tests/ut_utils/ut_utils.h diff --git a/firmware/src/hw/analog.c b/firmware/src/hw/analog.c deleted file mode 100644 index 8f8a291..0000000 --- a/firmware/src/hw/analog.c +++ /dev/null @@ -1,45 +0,0 @@ -/**** Includes ****/ -#include "board/utils/utils.h" -#include "board/ain.h" -#include "analog.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t ain_mapping(uint8_t analog_ch, uint8_t* ain_ch); - -/**** Public function definitions ****/ -uint16_t analog_ch_get(uint8_t analog_ch) -{ - uint8_t ain_ch; - // Get channel config - if(ain_mapping(analog_ch, &ain_ch)) return 0; - - // Read input as mV - uint16_t ch_mv = bsp_ain_read(ain_ch); - - // Return result - return ch_mv; -} - -/**** Private function definitions ****/ -static uint8_t ain_mapping(uint8_t analog_ch, uint8_t* ain_ch) -{ - switch(analog_ch) - { - case ANALOG_1: // Pot - *ain_ch = BSP_AIN2; - return 0; - - case ANALOG_2: // Mode - *ain_ch = BSP_AIN1; - return 0; - - default: //Invalid channel - *ain_ch = BSP_AIN5; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/analog.h b/firmware/src/hw/analog.h deleted file mode 100644 index c09021b..0000000 --- a/firmware/src/hw/analog.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ANALOG_H_ -#define ANALOG_H_ - -/* -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define ANALOG_1 1 //Potentiometer -#define ANALOG_2 2 //Mode - -/**** Public function declarations ****/ -uint16_t analog_ch_get(uint8_t analog_ch); - -#ifdef TESTING -#endif - -#endif /* ANALOG_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/ain.c b/firmware/src/hw/board/ain.c deleted file mode 100644 index a7c6230..0000000 --- a/firmware/src/hw/board/ain.c +++ /dev/null @@ -1,77 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "ain.h" - -/**** Private definitions ****/ -typedef struct { - uint8_t adc_ch; - uint8_t mul; - uint8_t div; - int16_t offset; -} ainchcfg_t; - -/**** Private constants ****/ -static const uint8_t MV_MUL = BSP_AIN_DEF_MV_MUL; -static const uint8_t MV_DIV = BSP_AIN_DEF_MV_DIV; -static const int16_t MV_OFFSET = BSP_AIN_DEF_MV_OFFSET; - -/**** Private variables ****/ - -/**** Private function declarations ****/ -static uint8_t ain_mapping(uint8_t ain_ch, ainchcfg_t* ain_ch_cfg); - -/**** Public function definitions ****/ -uint16_t bsp_ain_read(uint8_t ch) -{ - ainchcfg_t cfg; - // Get analog input config, and check validity - if(ain_mapping(ch, &cfg)) return 0; - - //Read ADC - uint16_t raw = mcu_adc_read(cfg.adc_ch); - - //Convert to mV - raw = util_convert_muldivoff(raw, cfg.mul, cfg.div, cfg.offset); - - // Return result - return raw; -} - -/**** Private function definitions ****/ -static uint8_t ain_mapping(uint8_t ain_ch, ainchcfg_t* ain_ch_cfg) -{ - // Default 10bit ADC with 5V reference to mV conversion - ain_ch_cfg->mul = MV_MUL; - ain_ch_cfg->div = MV_DIV; - ain_ch_cfg->offset = MV_OFFSET; - - switch(ain_ch) - { - case BSP_AIN1: // Mode - ain_ch_cfg->adc_ch = MCU_ADC5; - return 0; - - case BSP_AIN2: // Pot - ain_ch_cfg->adc_ch = MCU_ADC4; - return 0;; - - case BSP_AIN3: // MCU Temp - ain_ch_cfg->adc_ch = MCU_ADC8; - return 0; - - case BSP_AIN4: // MCU Internal reference - ain_ch_cfg->adc_ch = MCU_ADC14; - return 0; - - case BSP_AIN5: // MCU Ground - ain_ch_cfg->adc_ch = MCU_ADC15; - return 0; - - default: // Invalid channel - ain_ch_cfg->adc_ch = MCU_ADC15; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/board/ain.h b/firmware/src/hw/board/ain.h deleted file mode 100644 index a973f8c..0000000 --- a/firmware/src/hw/board/ain.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef AIN_H_ -#define AIN_H_ - -/* -AIN1 MODE -AIN2 POT -*/ - -/**** Includes ****/ -#include -#include "config.h" - -/**** Public definitions ****/ -#define BSP_AIN1 1 // Mode -#define BSP_AIN2 2 // Pot -#define BSP_AIN3 3 // MCU Temp -#define BSP_AIN4 4 // MCU Internal reference -#define BSP_AIN5 5 // MCU Ground - -/**** Public function declarations ****/ -uint16_t bsp_ain_read(uint8_t ch); - -#ifdef TESTING -#endif - -#endif /* AIN_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/config.h b/firmware/src/hw/board/config.h deleted file mode 100644 index 851b087..0000000 --- a/firmware/src/hw/board/config.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef BSP_CONFIG_H_ -#define BSP_CONFIG_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define BSP_AIN_DEF_MV_MUL 215 -#define BSP_AIN_DEF_MV_DIV 44 -#define BSP_AIN_DEF_MV_OFFSET 0 - -#define BSP_HB_MV_MUL BSP_AIN_DEF_MV_MUL -#define BSP_HB_MV_DIV BSP_AIN_DEF_MV_DIV -#define BSP_HB_MV_OFFSET BSP_AIN_DEF_MV_OFFSET - -#define BSP_HB_UDIV_MUL 20 -#define BSP_HB_UDIV_DIV 1 -#define BSP_HB_UDIV_OFFSET 0 - -#define BSP_HB_ISUP_MUL 235 -#define BSP_HB_ISUP_DIV 6 -#define BSP_HB_ISUP_OFFSET 0 - -#define BSP_HB_IOUT_MUL 215 -#define BSP_HB_IOUT_DIV 22 -#define BSP_HB_IOUT_OFFSET 0 - -/**** Public function declarations ****/ -#ifdef TESTING -#endif - -#endif /* BSP_CONFIG_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/din.c b/firmware/src/hw/board/din.c deleted file mode 100644 index 3476e11..0000000 --- a/firmware/src/hw/board/din.c +++ /dev/null @@ -1,83 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "din.h" - -/**** Private definitions ****/ -typedef struct { - uint8_t gpio_ch; - uint8_t invert; -} dinchcfg_t; - -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t din_mapping(uint8_t din_ch, dinchcfg_t* din_ch_cfg); - -/**** Public function definitions ****/ -uint8_t bsp_din_read(uint8_t ch) -{ - dinchcfg_t cfg; - // Get digital input config, and check validity - if(din_mapping(ch, &cfg)) return BSP_DIN_LOW; - - //Read GPIO - uint8_t raw = mcu_gpio_read(cfg.gpio_ch); - - // Check config and invert - if(cfg.invert) raw = util_invert_8b(raw); - - // Return result - return raw; -} - -/**** Private function definitions ***/ -static uint8_t din_mapping(uint8_t din_ch, dinchcfg_t* din_ch_cfg) -{ - // By default ch is not inverted - din_ch_cfg->invert = 0; - - switch(din_ch) - { - case BSP_DIN1: //Mode - din_ch_cfg->gpio_ch = MCU_GPIO0; - return 0; - - case BSP_DIN2: //Pot - din_ch_cfg->gpio_ch = MCU_GPIO1; - return 0; - - case BSP_DIN3: //Down - din_ch_cfg->gpio_ch = MCU_GPIO2; - return 0; - - case BSP_DIN4: //Up - din_ch_cfg->gpio_ch = MCU_GPIO3; - return 0; - - case BSP_DIN5: //Dimm - din_ch_cfg->gpio_ch = MCU_GPIO4; - din_ch_cfg->invert = 1; - return 0; - - case BSP_DIN6: //Brakes - din_ch_cfg->gpio_ch = MCU_GPIO5; - din_ch_cfg->invert = 1; - return 0; - - case BSP_DIN7: //Handbrake - din_ch_cfg->gpio_ch = MCU_GPIO6; - din_ch_cfg->invert = 1; - return 0; - - case BSP_DIN7N: //Handbrake pull - din_ch_cfg->gpio_ch = MCU_GPIO7; - return 0; - - default: //Invalid channel - din_ch_cfg->gpio_ch = 0; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/board/din.h b/firmware/src/hw/board/din.h deleted file mode 100644 index 6a74fe2..0000000 --- a/firmware/src/hw/board/din.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef DIN_H_ -#define DIN_H_ - -/* -DIN1 MODE -DIN2 POT -DIN3 DOWN -DIN4 UP -DIN5 HV DIM -DIN6 HV BRAKES -DIN7 HV HANDBRAKE -DIN8 HBRAKE PULL -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define BSP_DIN1 1 -#define BSP_DIN2 2 -#define BSP_DIN3 3 -#define BSP_DIN4 4 -#define BSP_DIN5 5 -#define BSP_DIN6 6 -#define BSP_DIN7 7 -#define BSP_DIN7N 8 - -#define BSP_DIN_LOW 0 -#define BSP_DIN_HIGH 1 - -/**** Public function declarations ****/ -uint8_t bsp_din_read(uint8_t ch); - -#ifdef TESTING -#endif - -#endif /* DIN_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/dout.c b/firmware/src/hw/board/dout.c deleted file mode 100644 index 9a5214c..0000000 --- a/firmware/src/hw/board/dout.c +++ /dev/null @@ -1,58 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "dout.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t dout_mapping(uint8_t dout_ch, uint8_t* gpio_ch); - -/**** Public function definitions ****/ -void bsp_dout_write(uint8_t ch, int8_t lvl) -{ - uint8_t gpio_ch; - // Get digital input config, and check validity - if(dout_mapping(ch, &gpio_ch)) return; - - // Write GPIO - mcu_gpio_write(gpio_ch, lvl); -} - -/**** Private function definitions ***/ -static uint8_t dout_mapping(uint8_t dout_ch, uint8_t* gpio_ch) -{ - switch(dout_ch) - { - case BSP_DOUT1: //Mode - *gpio_ch = MCU_GPIO0; - return 0; - - case BSP_DOUT2: //Pot - *gpio_ch = MCU_GPIO1; - return 0; - - case BSP_DOUT3: //Down - *gpio_ch = MCU_GPIO2; - return 0; - - case BSP_DOUT4: //Up - *gpio_ch = MCU_GPIO3; - return 0; - - case BSP_DOUT5: //Handbrake pull - *gpio_ch = MCU_GPIO7; - return 0; - - case BSP_DOUT6: //Speed pull - *gpio_ch = MCU_GPIO8; - return 0; - - default: //Invalid channel - *gpio_ch = 0; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/board/dout.h b/firmware/src/hw/board/dout.h deleted file mode 100644 index adbbef8..0000000 --- a/firmware/src/hw/board/dout.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef DOUT_H_ -#define DOUT_H_ - -/* -DOUT1 MODE -DOUT2 POT -DOUT3 DOWN -DOUT4 UP -DOUT5 HBRAKE PULL -DOUT6 SPEED PULL -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define BSP_DOUT1 1 -#define BSP_DOUT2 2 -#define BSP_DOUT3 3 -#define BSP_DOUT4 4 -#define BSP_DOUT5 5 -#define BSP_DOUT6 6 - -#define BSP_DOUT_LOW 0 -#define BSP_DOUT_HIGH 1 -#define BSP_DOUT_HIZ -1 - -/**** Public function declarations ****/ -void bsp_dout_write(uint8_t ch, int8_t lvl); - -#ifdef TESTING -#endif - -#endif /* DOUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/halfbridge.c b/firmware/src/hw/board/halfbridge.c deleted file mode 100644 index 3789d03..0000000 --- a/firmware/src/hw/board/halfbridge.c +++ /dev/null @@ -1,179 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "halfbridge.h" - -/**** Private definitions ****/ -typedef struct { - uint8_t adc_ch; - uint8_t mul; - uint8_t div; - int16_t offset; -} ainchcfg_t; - -typedef enum { - AIN_OUT_VOLTAGE, - AIN_OUT_CURRENT, - AIN_SUP_VOLTAGE, - AIN_SUP_CURRENT -} hb_ainch_t; - -/**** Private constants ****/ -// Analog channels conversion coefficients -static const uint8_t MV_MUL = BSP_HB_MV_MUL; -static const uint8_t MV_DIV = BSP_HB_MV_DIV; -static const int16_t MV_OFFSET = BSP_HB_MV_OFFSET; - -static const uint8_t UDIV_MUL = BSP_HB_UDIV_MUL; -static const uint8_t UDIV_DIV = BSP_HB_UDIV_DIV; -static const int16_t UDIV_OFFSET = BSP_HB_UDIV_OFFSET; - -static const uint8_t ISUP_MUL = BSP_HB_ISUP_MUL; -static const uint8_t ISUP_DIV = BSP_HB_ISUP_DIV; -static const int16_t ISUP_OFFSET = BSP_HB_ISUP_OFFSET; - -static const uint8_t IOUT_MUL = BSP_HB_IOUT_MUL; -static const uint8_t IOUT_DIV = BSP_HB_IOUT_DIV; -static const int16_t IOUT_OFFSET = BSP_HB_IOUT_OFFSET; - -/**** Private variables ****/ - -/**** Mapping function declarations ****/ -static uint8_t hb_pwm_mapping(uint8_t* pwm_ch); -static uint8_t hb_low_side_mapping(uint8_t* gpio_ch); -static uint8_t hb_ain_mapping(hb_ainch_t ain_ch, ainchcfg_t* ain_ch_cfg); - -/**** Private function declarations ****/ -static uint16_t limit_pwm(uint16_t pwm_in); -static uint16_t read_ain(hb_ainch_t ch); - -/**** Public function definitions ****/ -void bsp_hb_write_low(uint8_t state) -{ - uint8_t gpio_ch; - // Get GPIO channel, and check validity - if(hb_low_side_mapping(&gpio_ch)) return; - - // Set low side on or off - if(state) mcu_gpio_write(gpio_ch, MCU_GPIO_HIGH); - else mcu_gpio_write(gpio_ch, MCU_GPIO_LOW); -} - -void bsp_hb_write_pwm(uint16_t pwm) -{ - uint8_t pwm_ch; - // Get PWM channel, and check validity - if(hb_pwm_mapping(&pwm_ch)) return; - - // Limit PWM, because of charge pump - pwm = limit_pwm(pwm); - - // Set pwm - mcu_pwm_write(pwm_ch, pwm); -} - -void bsp_hb_read_meas(hb_meas_t* measurements) -{ - // Read analog inputs - measurements->out_voltage = read_ain(AIN_OUT_VOLTAGE); - measurements->out_current = read_ain(AIN_OUT_CURRENT); - measurements->sup_voltage = read_ain(AIN_SUP_VOLTAGE); - measurements->sup_current = read_ain(AIN_SUP_CURRENT); - - // Calculate secondary measurements - measurements->out_power = util_sat_mul_kilo(measurements->out_voltage, measurements->out_current); - measurements->sup_power = util_sat_mul_kilo(measurements->sup_voltage, measurements->sup_current); - - measurements->out_impedance = util_sat_div_kilo(measurements->out_voltage, measurements->out_current); - - uint8_t ch; - //Read low side control GPIO level - if(hb_low_side_mapping(&ch)) measurements->low_side_ctrl = 0; - else measurements->low_side_ctrl = mcu_gpio_read(ch); - - //Read PWM duty cycle in 16b format - if(hb_pwm_mapping(&ch)) measurements->pwm = 0; - else measurements->pwm = mcu_pwm_read(ch); -} - -/**** Private function declarations ****/ -static uint16_t read_ain(hb_ainch_t ch) -{ - ainchcfg_t cfg; - // Get analog input config, and check validity - if(hb_ain_mapping(ch, &cfg)) return 0; - - //Read ADC - uint16_t raw = mcu_adc_read(cfg.adc_ch); - - //Convert to target units - raw = util_convert_muldivoff(raw, cfg.mul, cfg.div, cfg.offset); - - // Return result - return raw; -} - -static uint16_t limit_pwm(uint16_t pwm_in) -{ - // Limit to ~95% - if (pwm_in > 0xFC00) return 0xFC00; - else return pwm_in; -} - -/**** Mapping function definitions ****/ -static uint8_t hb_pwm_mapping(uint8_t* pwm_ch) -{ - *pwm_ch = MCU_PWM0; - return 0; -} - -static uint8_t hb_low_side_mapping(uint8_t* gpio_ch) -{ - *gpio_ch = MCU_GPIO15; - return 0; -} - -static uint8_t hb_ain_mapping(hb_ainch_t ain_ch, ainchcfg_t* ain_ch_cfg) -{ - switch(ain_ch) - { - case AIN_OUT_VOLTAGE: - ain_ch_cfg->adc_ch = MCU_ADC1; - ain_ch_cfg->mul = UDIV_MUL; - ain_ch_cfg->div = UDIV_DIV; - ain_ch_cfg->offset = UDIV_OFFSET; - return 0; - - case AIN_OUT_CURRENT: - ain_ch_cfg->adc_ch = MCU_ADC0; - ain_ch_cfg->mul = IOUT_MUL; - ain_ch_cfg->div = IOUT_DIV; - ain_ch_cfg->offset = IOUT_OFFSET; - return 0; - - case AIN_SUP_VOLTAGE: - ain_ch_cfg->adc_ch = MCU_ADC2; - ain_ch_cfg->mul = UDIV_MUL; - ain_ch_cfg->div = UDIV_DIV; - ain_ch_cfg->offset = UDIV_OFFSET; - return 0; - - case AIN_SUP_CURRENT: - ain_ch_cfg->adc_ch = MCU_ADC3; - ain_ch_cfg->mul = ISUP_MUL; - ain_ch_cfg->div = ISUP_DIV; - ain_ch_cfg->offset = ISUP_OFFSET; - return 0; - - default: //Invalid channel - ain_ch_cfg->adc_ch = MCU_ADC15; - // Default 10bit ADC with 5V reference to mV conversion - ain_ch_cfg->mul = MV_MUL; - ain_ch_cfg->div = MV_DIV; - ain_ch_cfg->offset = MV_OFFSET; - return 1; - } - - return 1; -} - diff --git a/firmware/src/hw/board/halfbridge.h b/firmware/src/hw/board/halfbridge.h deleted file mode 100644 index bf5e1a7..0000000 --- a/firmware/src/hw/board/halfbridge.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef HALFBRIDGE_H_ -#define HALFBRIDGE_H_ - -/* -*/ - -/**** Includes ****/ -#include -#include "config.h" - -/**** Public definitions ****/ -typedef struct { - uint16_t out_voltage; - uint16_t out_current; - uint16_t sup_voltage; - uint16_t sup_current; - uint16_t out_power; - uint16_t sup_power; - uint16_t out_impedance; - uint8_t low_side_ctrl; - uint16_t pwm; -} hb_meas_t; - -/**** Public function declarations ****/ -void bsp_hb_write_low(uint8_t state); -void bsp_hb_write_pwm(uint16_t pwm); - -// Feedback functions -void bsp_hb_read_meas(hb_meas_t* measurements); - -#ifdef TESTING -#endif - -#endif /* HALFBRIDGE_H_ */ diff --git a/firmware/src/hw/board/mcu/mcu_hal.h b/firmware/src/hw/board/mcu/mcu_hal.h deleted file mode 100644 index 010fad2..0000000 --- a/firmware/src/hw/board/mcu/mcu_hal.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef MCU_HAL_H_ -#define MCU_HAL_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -/* -GPIO0 Down -GPIO1 Up -GPIO2 Mode -GPIO3 Handbrake -GPIO4 Brakes -GPIO5 Dimm -GPIO6 LED0 -GPIO7 LED1 -GPIO8 LED2 -GPIO9 LED3 -GPIO10 LED4 -GPIO11 LED5 -GPIO12 DCCD Enable -GPIO13 Handbrake pull -GPIO14 Speed pull -GPIO15 DCCD PWM -GPIO16 LED PWM - -ADC0 Output current -ADC1 Output voltage -ADC2 Battery current -ADC3 Battery voltage -ADC4 Potentiometer -ADC5 Mode -ADC8 MCU temperature -ADC14 MCU internal reference -ADC15 MCU ground -*/ - -#define MCU_GPIO0 0 //Mode -#define MCU_GPIO1 1 //Pot -#define MCU_GPIO2 2 //Down -#define MCU_GPIO3 3 //Up -#define MCU_GPIO4 4 //Dimm -#define MCU_GPIO5 5 //Brakes -#define MCU_GPIO6 6 //Handbrake -#define MCU_GPIO7 7 //Handbrake pull -#define MCU_GPIO8 8 //Speed pull -#define MCU_GPIO9 9 //LED0 -#define MCU_GPIO10 10 //LED1 -#define MCU_GPIO11 11 //LED2 -#define MCU_GPIO12 12 //LED3 -#define MCU_GPIO13 13 //LED4 -#define MCU_GPIO14 14 //LED5 -#define MCU_GPIO15 15 //DCCD Enable -#define MCU_GPIO16 16 //DCCD PWM -#define MCU_GPIO17 17 //LED PWM - -#define MCU_GPIO_LOW 0 -#define MCU_GPIO_HIGH 1 -#define MCU_GPIO_HIZ -1 - -#define MCU_ADC0 0 //Output current -#define MCU_ADC1 1 //Output voltage -#define MCU_ADC2 2 //Battery voltage -#define MCU_ADC3 3 //Battery current -#define MCU_ADC4 4 //Potentiometer -#define MCU_ADC5 5 //Mode -#define MCU_ADC8 8 //MCU temperature -#define MCU_ADC14 14 //MCU internal reference -#define MCU_ADC15 15 //MCU ground - -#define MCU_PWM0 0 //DCCD -#define MCU_PWM1 1 //LED - -//ADC definitions -typedef enum { - MCU_ADC_DIV2 = 0x01, - MCU_ADC_DIV4 = 0x02, - MCU_ADC_DIV8 = 0x03, - MCU_ADC_DIV16 = 0x04, - MCU_ADC_DIV32 = 0x05, - MCU_ADC_DIV64 = 0x06, - MCU_ADC_DIV128 = 0x07 -} adcClkDiv_t; - -//Timer definitions -typedef enum { - MCU_TIM_DIV1 = 0x01, - MCU_TIM_DIV8 = 0x02, - MCU_TIM_DIV64 = 0x03, - MCU_TIM_DIV256 = 0x04, - MCU_TIM_DIV1024 = 0x05 -} timerClkDiv_t; - -typedef struct { - adcClkDiv_t adc_clk; - timerClkDiv_t pwm_clk; - uint16_t pwm_top; - uint8_t pwm_chb_en; -} startupCfg_t; - -/**** Public function declarations ****/ -void mcu_startup(startupCfg_t* hwCfg); - -uint8_t mcu_gpio_read(uint8_t ch); -void mcu_gpio_write(uint8_t ch, int8_t lvl); - -uint16_t mcu_adc_read(uint8_t ch); - -void mcu_pwm_write(uint8_t ch, uint16_t dc); -uint16_t mcu_pwm_read(uint8_t ch); - -uint8_t mcu_ee_read8b(uint16_t address); -uint16_t mcu_ee_read16b(uint16_t address); -uint32_t mcu_ee_read32b(uint16_t address); - -void mcu_ee_write8b(uint16_t address, uint8_t value); -void mcu_ee_write16b(uint16_t address, uint16_t value); -void mcu_ee_write32b(uint16_t address, uint32_t value); - -#endif /* MCU_HAL_H_ */ diff --git a/firmware/src/hw/board/mcu/mcu_hal_r8.c b/firmware/src/hw/board/mcu/mcu_hal_r8.c deleted file mode 100644 index bd1bcfe..0000000 --- a/firmware/src/hw/board/mcu/mcu_hal_r8.c +++ /dev/null @@ -1,418 +0,0 @@ -/**** Includes ****/ -#include -#include -#include "mcu_hal.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t gpio_read(uint8_t pin_reg, uint8_t mask); -static void pwm_write_ocx(uint8_t ch, uint16_t value); -static uint16_t pwm_read_ocx(uint8_t ch); - -/**** Public function definitions ****/ -void mcu_startup(startupCfg_t* hwCfg) -{ - // Fail-safe GPIO init - PORTB = 0xF8; // Set PORTB pull-ups - DDRB = 0x00; // Set all as inputs - - PORTC = 0x40; // Set PORTC pull-ups - DDRC = 0x00; // Set all as inputs - - PORTD = 0x80; // Set PORTD pull-ups - DDRD = 0x00; // Set all as inputs - - PORTE = 0x0A; // Set PORTE pull-ups - DDRE = 0x00; // Set all as inputs - - // Half-bridge related pins - PORTB &= ~0x03; //Set low - DDRB |= 0x03; //Set as output - - // Common OD PWM pin - if(hwCfg->pwm_chb_en) PORTB &= ~0x04; //Set low - else PORTB |= 0x04; //Set high - DDRB |= 0x04; //Set as output - - // OD control pins - PORTD &= ~0x3F; //Set low (off) - DDRD |= 0x3F; //Set as outputs - - // Handbrake pull-up pin - PORTB |= 0x20; //Set high - DDRB |= 0x20; //Set as output - - // Handbrake and brakes pins - PORTB |= 0xC0; //Set pull-up on - DDRB &= ~0xC0; //Set as inputs - - // Dimm - PORTD |= 0x80; //Set pull-up on - DDRD &= ~0x80; //Set as input - - // Up and Down - PORTE |= 0x0A; //Set pull-up on - DDRE &= ~0x0A; //Set as inputs - - // Internal ADC inputs - PORTC &= ~0x0F; //Pull-up off - DDRC &= ~0x0F; //Set as inputs - - // Potentiometer & Mode - PORTC &= ~0x30; //Pull-up off - DDRC &= ~0x30; //Set as inputs - - //ADC configuration - PRR0 &= ~0x01; //Enable ADC power - DIDR0 |= 0x0F; //Disable digital inputs, ADC0-ADC3 - - ADMUX = 0x40; //Set AVCC reference, Right adjust - ADCSRA = 0x00; //ADC Disabled, Single conversion, no IT - ADCSRA |= (uint8_t)hwCfg->adc_clk; - ADCSRB = 0x00; //no trigger input - - ADCSRA |= 0x80; //Enable ADC - - //DCCD and LED PWM configuration - PRR0 &= ~0x80; //Enable Timer1 power - TCCR1A = 0xC2; //Connect OC1A, inverted mode - if(hwCfg->pwm_chb_en) TCCR1A |= 0x30; //Connect OC1B, inverted mode - TCCR1B = 0x18; //PWM, Phase & Frequency Correct ICR1 top, no clock, WGM:0xE - TCCR1C = 0x00; - TCNT1 = 0x0000; - OCR1A = 0xFFFF; - OCR1B = 0xFFFF; - ICR1 = hwCfg->pwm_top; - TIMSK1 = 0x00; //No interrupts - TIFR1 = 0x00; //Clear all flags - - uint8_t tim1_prescaler = (uint8_t)hwCfg->pwm_clk; - TCCR1B |= tim1_prescaler; //Enable timer -} - -// ADC Interface functions -uint16_t mcu_adc_read(uint8_t ch) -{ - //check if ADC is enabled - if(!(ADCSRA&0x80)) return 0xFFFF; - - //Safe guard mux - if(ch > 15) return 0xFFFF; - // Not available channels - if((ch > 8) && (ch<14)) return 0xFFFF; - - ADMUX &= ~0x0F; - ADMUX |= ch; - ADCSRA |= 0x40; - while(ADCSRA&0x40); //wait to finish - return ADC; -} - -// PWM Timer Interface functions -void mcu_pwm_write(uint8_t ch, uint16_t dc) -{ - dc = 0xFFFF - dc; - - // Calculate value as % of TOP - uint32_t top = (uint32_t)ICR1; - uint32_t temp = (uint32_t)dc * top; - temp = temp/0x0000FFFF; - - //Limit temp - if(temp>0x0000FFFF) temp = 0x0000FFFF; - uint16_t ocrx = (uint16_t)temp; - - // Write register - pwm_write_ocx(ch, ocrx); -} - -uint16_t mcu_pwm_read(uint8_t ch) -{ - uint16_t ocrx = pwm_read_ocx(ch); - - // Check easy answers - if(ocrx == 0) return 0; - if(ocrx >= ICR1) return 0xFFFF; - - // Calculate - uint32_t top = (uint32_t)ICR1; - uint32_t temp = (uint32_t)ocrx * 0xFFFF; - temp = temp/top; - - //Limit temp - if(temp>0x0000FFFF) return 0xFFFF; - return (uint16_t)temp; -} - -uint8_t mcu_gpio_read(uint8_t ch) -{ - switch(ch) - { - case MCU_GPIO0: // Mode DIN1 - return gpio_read(PINC,0x20); - - case MCU_GPIO1: // Pot DIN2 - return gpio_read(PINC,0x10); - - case MCU_GPIO2: // Down DIN3 - return gpio_read(PINE,0x02); - - case MCU_GPIO3: // Up DIN4 - return gpio_read(PINE,0x08); - - case MCU_GPIO4: // Dimm DIN5 - return gpio_read(PIND,0x80); - - case MCU_GPIO5: // Brakes DIN6 - return gpio_read(PINB,0x80); - - case MCU_GPIO6: // Handbrake DIN7 - return gpio_read(PINB,0x40); - - case MCU_GPIO7: // Handbrake pull DIN8 - return gpio_read(PINB,0x20); - - case MCU_GPIO8: // Speed-pull - return gpio_read(PIND,0x40); - - case MCU_GPIO9: // LED 0 - return gpio_read(PIND,0x01); - - case MCU_GPIO10: // LED 1 - return gpio_read(PIND,0x02); - - case MCU_GPIO11: // LED 2 - return gpio_read(PIND,0x04); - - case MCU_GPIO12: // LED 3 - return gpio_read(PIND,0x08); - - case MCU_GPIO13: // LED 4 - return gpio_read(PIND,0x10); - - case MCU_GPIO14: // LED 5 - return gpio_read(PIND,0x20); - - case MCU_GPIO15: // DCCD Enable - return gpio_read(PINB,0x01); - - case MCU_GPIO16: // DCCD PWM - return gpio_read(PINB,0x02); - - case MCU_GPIO17: // LED PWM - return gpio_read(PINB,0x04); - - default: - return 0; - } -} - -void mcu_gpio_write(uint8_t ch, int8_t lvl) -{ - switch(ch) - { - case MCU_GPIO0: // Mode DIN1 - if(lvl>0) - { - PORTC |= 0x20; - DDRC |= 0x20; - } - else if(lvl<0) - { - DDRC &= ~0x20; - PORTC &= ~0x20; - } - else - { - PORTC &= ~0x20; - DDRC |= 0x20; - } - return; - - case MCU_GPIO1: // Pot DIN2 - if(lvl>0) - { - PORTC |= 0x10; - DDRC |= 0x10; - } - else if(lvl<0) - { - DDRC &= ~0x10; - PORTC &= ~0x10; - } - else - { - PORTC &= ~0x10; - DDRC |= 0x10; - } - return; - - case MCU_GPIO2: // Down DIN3 - if(lvl>0) - { - PORTE |= 0x02; - DDRE |= 0x02; - } - else if(lvl<0) - { - DDRE &= ~0x02; - PORTE &= ~0x02; - } - else - { - PORTE &= ~0x02; - DDRE |= 0x02; - } - return; - - case MCU_GPIO3: // Up DIN4 - if(lvl>0) - { - PORTE |= 0x08; - DDRE |= 0x08; - } - else if(lvl<0) - { - DDRE &= ~0x08; - PORTE &= ~0x08; - } - else - { - PORTE &= ~0x08; - DDRE |= 0x08; - } - return; - - case MCU_GPIO7: // Handbrake pull DIN - if(lvl>0) - { - PORTB |= 0x20; - DDRB |= 0x20; - } - else if(lvl<0) - { - DDRB &= ~0x20; - PORTB &= ~0x20; - } - else - { - PORTB &= ~0x20; - DDRB |= 0x20; - } - return; - - case MCU_GPIO8: // Speed-pull - if(lvl>0) PORTD |= 0x40; - else PORTD &= ~0x40; - return; - - case MCU_GPIO9: // LED 0 - if(lvl>0) PORTD |= 0x01; - else PORTD &= ~0x01; - return; - - case MCU_GPIO10: // LED 1 - if(lvl>0) PORTD |= 0x02; - else PORTD &= ~0x02; - return; - - case MCU_GPIO11: // LED 2 - if(lvl>0) PORTD |= 0x04; - else PORTD &= ~0x04; - return; - - case MCU_GPIO12: // LED 3 - if(lvl>0) PORTD |= 0x08; - else PORTD &= ~0x08; - return; - - case MCU_GPIO13: // LED 4 - if(lvl>0) PORTD |= 0x10; - else PORTD &= ~0x10; - return; - - case MCU_GPIO14: // LED 5 - if(lvl>0) PORTD |= 0x20; - else PORTD &= ~0x20; - return; - - case MCU_GPIO15: // DCCD Enable - if(lvl>0) PORTB |= 0x01; - else PORTB &= ~0x01; - return; - - default: - return; - } -} - -uint8_t mcu_ee_read8b(uint16_t address) -{ - return eeprom_read_byte((uint8_t*)address); -} - -uint16_t mcu_ee_read16b(uint16_t address) -{ - return eeprom_read_word((uint16_t*)address); -} - -uint32_t mcu_ee_read32b(uint16_t address) -{ - return eeprom_read_dword((uint32_t*)address); -} - -void mcu_ee_write8b(uint16_t address, uint8_t value) -{ - eeprom_write_byte((uint8_t*)address, value); -} - -void mcu_ee_write16b(uint16_t address, uint16_t value) -{ - eeprom_write_word((uint16_t*)address, value); -} - -void mcu_ee_write32b(uint16_t address, uint32_t value) -{ - eeprom_write_dword((uint32_t*)address, value); -} - -/**** Private function definitions ****/ -static uint8_t gpio_read(uint8_t pin_reg, uint8_t mask) -{ - if(pin_reg&mask) return MCU_GPIO_HIGH; - else return MCU_GPIO_LOW; -} - -static void pwm_write_ocx(uint8_t ch, uint16_t value) -{ - switch(ch) - { - case MCU_PWM0: - OCR1A = value; - return; - - case MCU_PWM1: - OCR1B = value; - return; - - default: - return; - } -} - -static uint16_t pwm_read_ocx(uint8_t ch) -{ - switch(ch) - { - case MCU_PWM0: - return OCR1A; - - case MCU_PWM1: - return OCR1B ; - - default: - return 0x0000; - } -} \ No newline at end of file diff --git a/firmware/src/hw/board/odout.c b/firmware/src/hw/board/odout.c deleted file mode 100644 index f5fda12..0000000 --- a/firmware/src/hw/board/odout.c +++ /dev/null @@ -1,80 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "odout.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t odout_pwm_mapping(uint8_t* pwm_ch); -static uint8_t odout_mapping(uint8_t od_ch, uint8_t* gpio_ch); - - -/**** Public function definitions ****/ -void bsp_odout_write(uint8_t ch, int8_t lvl) -{ - uint8_t gpio_ch; - // Get GPIO channel config, and check validity - if(odout_mapping(ch, &gpio_ch)) return; - - // Set output level - if(lvl==0) mcu_gpio_write(gpio_ch, 1); // Output active - else mcu_gpio_write(gpio_ch, 0); // Output off -} - -void bsp_odout_write_common(uint8_t percent) -{ - uint8_t pwm_ch; - // Get PWM channel config, and check validity - if(odout_pwm_mapping(&pwm_ch)) return; - - // Convert percent to 16b duty cycle - uint16_t dc = util_percent_to_16b(percent); - - // Set PWM - mcu_pwm_write(pwm_ch, dc); -} - -/**** Private function definitions ****/ -static uint8_t odout_pwm_mapping(uint8_t* pwm_ch) -{ - *pwm_ch = MCU_PWM1; - return 0; -} - -static uint8_t odout_mapping(uint8_t od_ch, uint8_t* gpio_ch) -{ - switch(od_ch) - { - case BSP_OD1: // LED0 - *gpio_ch = MCU_GPIO9; - return 0; - - case BSP_OD2: // LED1 - *gpio_ch = MCU_GPIO10; - return 0; - - case BSP_OD3: // LED2 - *gpio_ch = MCU_GPIO11; - return 0; - - case BSP_OD4: // LED3 - *gpio_ch = MCU_GPIO12; - return 0; - - case BSP_OD5: // LED4 - *gpio_ch = MCU_GPIO13; - return 0; - - case BSP_OD6: // LED5 - *gpio_ch = MCU_GPIO14; - return 0; - - default: //Invalid channel - *gpio_ch = 0; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/board/odout.h b/firmware/src/hw/board/odout.h deleted file mode 100644 index 0d7ad5e..0000000 --- a/firmware/src/hw/board/odout.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ODOUT_H_ -#define ODOUT_H_ - -/* -OD1 LED 0 -OD2 LED 1 -OD3 LED 2 -OD4 LED 3 -OD5 LED 4 -OD6 LED 5 - -COMMON LED PWM -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define BSP_OD1 1 -#define BSP_OD2 2 -#define BSP_OD3 3 -#define BSP_OD4 4 -#define BSP_OD5 5 -#define BSP_OD6 6 - -#define BSP_ODOUT_LOW 0 -#define BSP_ODOUT_HIGH 1 -#define BSP_ODOUT_HIZ -1 - -/**** Public function declarations ****/ -void bsp_odout_write(uint8_t ch, int8_t lvl); -void bsp_odout_write_common(uint8_t percent); - -#ifdef TESTING -#endif - -#endif /* ODOUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/setup.c b/firmware/src/hw/board/setup.c deleted file mode 100644 index 5f13cc4..0000000 --- a/firmware/src/hw/board/setup.c +++ /dev/null @@ -1,23 +0,0 @@ -/**** Includes ****/ -#include "utils/utils.h" -#include "mcu/mcu_hal.h" -#include "setup.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ - -/**** Private function definitions ****/ -void bsp_startup(void) -{ - startupCfg_t mcu_cfg; - - mcu_cfg.adc_clk = MCU_ADC_DIV2; - mcu_cfg.pwm_clk = MCU_TIM_DIV1; - mcu_cfg.pwm_top = 511; - mcu_cfg.pwm_chb_en = 1; - - mcu_startup(&mcu_cfg); -} \ No newline at end of file diff --git a/firmware/src/hw/board/setup.h b/firmware/src/hw/board/setup.h deleted file mode 100644 index c9e5bf8..0000000 --- a/firmware/src/hw/board/setup.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef BSP_SETUP_H_ -#define BSP_SETUP_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -/**** Public function declarations ****/ -void bsp_startup(void); - -#ifdef TESTING -#endif - -#endif /* BSP_SETUP_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/utils/faults.c b/firmware/src/hw/board/utils/faults.c deleted file mode 100644 index 02a04e5..0000000 --- a/firmware/src/hw/board/utils/faults.c +++ /dev/null @@ -1,67 +0,0 @@ -/**** Includes ****/ -#include "faults.h" -#include "utils.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Mapping function declarations ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -#ifdef TESTING -#endif - -uint8_t fault_process(fault_t* fault, uint8_t w_trig, uint8_t f_trig, fault_cfg_t* cfg) -{ - // Override warning trigger in case of fault trigger - if(f_trig) w_trig = 1; - - // Increase warning time, if reoccurring - if((w_trig)&&(fault->severity != FAULT_LVL_OK)) fault->w_time = util_sat_add_16b(fault->w_time, 1); - else fault->w_time = 0; - - // Check if waring can be considered fault - if((cfg->wtof > 0)&&(fault->w_time > cfg->wtof)) f_trig = 1; - - // Increase fault time, if reoccurring - if((f_trig)&&(fault->severity != FAULT_LVL_OK)) fault->f_time = util_sat_add_16b(fault->f_time, 1); - else fault->f_time = 0; - - // Modify fault trigger - if((cfg->delay)&&(fault->f_time < cfg->delay)) f_trig = 0; - - // Process fault level - if(f_trig) - { - fault->severity = FAULT_LVL_FAULT; - return 1; - } - else - { - if(w_trig) fault->severity = FAULT_LVL_WARNING; - else fault->severity = FAULT_LVL_OK; - } - - return 0; -} - -uint8_t fault_is_active(fault_t* fault) -{ - if(fault->severity == FAULT_LVL_FAULT) return 1; - else return 0; -} - -uint8_t fault_is_warning(fault_t* fault) -{ - if(fault->severity != FAULT_LVL_OK) return 1; - else return 0; -} - -void fault_reset(fault_t* fault) -{ - fault->severity = FAULT_LVL_OK; - fault->w_time = 0; - fault->f_time = 0; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/board/utils/faults.h b/firmware/src/hw/board/utils/faults.h deleted file mode 100644 index 2a89077..0000000 --- a/firmware/src/hw/board/utils/faults.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef FAULTS_H_ -#define FAULTS_H_ - -/* -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -typedef enum { - FAULT_LVL_OK, - FAULT_LVL_WARNING, - FAULT_LVL_FAULT -} fault_lvl_t; - -typedef struct { - fault_lvl_t severity; - uint16_t w_time; - uint16_t f_time; -} fault_t; - -typedef struct { - uint16_t delay; - uint16_t wtof; -} fault_cfg_t; - -/**** Public function declarations ****/ -uint8_t fault_process(fault_t* fault, uint8_t w_trig, uint8_t f_trig, fault_cfg_t* cfg); -uint8_t fault_is_active(fault_t* fault); -uint8_t fault_is_warning(fault_t* fault); -void fault_reset(fault_t* fault); - -#ifdef TESTING -#endif - -#endif /* FAULTS_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/utils/fuses.c b/firmware/src/hw/board/utils/fuses.c deleted file mode 100644 index 997f586..0000000 --- a/firmware/src/hw/board/utils/fuses.c +++ /dev/null @@ -1,81 +0,0 @@ -/**** Includes ****/ -#include "fuses.h" -#include "utils.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Mapping function declarations ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -#ifdef TESTING -#endif - -void fuse_reset(fuse_t* fuse) -{ - fuse->state = FUSE_OFF; - fuse->timer = 0; - fuse->count = 0; -} - -uint8_t fuse_process(fuse_t* fuse, uint8_t fault, fuse_cfg_t* cfg) -{ - // Active fault condition - if(fault) - { - // Note fuse time count - if((fuse->state == FUSE_OFF)||(fuse->state == FUSE_RETRY)) fuse->count++; - - // Go to fused state in any case - fuse->state = FUSE_ACTIVE; - fuse->timer = 0; - return 1; - }; - - // No active fault condition - if(fuse->state==FUSE_ACTIVE) - { - // Go to cooldown - fuse->state = FUSE_COOLDOWN; - fuse->timer = cfg->cooldown_time; - }; - - // Wait for timeout - if(fuse->timer) - { - fuse->timer--; - if(fuse->state == FUSE_RETRY) return 0; - else return 1; - }; - - // Timeout end, transition logic - switch(fuse->state) - { - case FUSE_COOLDOWN: - // Cooldown end - if(cfg->retry_time) - { - fuse->state = FUSE_RETRY; - fuse->timer = cfg->retry_time; - } - else - { - fuse->state = FUSE_OFF; - fuse->timer = 0; - } - break; - - case FUSE_RETRY: - // Go back to normal - fuse->state = FUSE_OFF; - break; - - default: - // Nothing to do - break; - } - - return 0; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/board/utils/fuses.h b/firmware/src/hw/board/utils/fuses.h deleted file mode 100644 index 49f3ce8..0000000 --- a/firmware/src/hw/board/utils/fuses.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FUSES_H_ -#define FUSES_H_ - -/* -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -typedef enum { - FUSE_OFF, - FUSE_ACTIVE, - FUSE_COOLDOWN, - FUSE_RETRY -} fuse_state_t; - -typedef struct { - fuse_state_t state; - uint8_t count; - uint16_t timer; -} fuse_t; - -typedef struct { - uint16_t cooldown_time; - uint16_t retry_time; -} fuse_cfg_t; - -/**** Public function declarations ****/ -void fuse_reset(fuse_t* fuse); -uint8_t fuse_process(fuse_t* fuse, uint8_t fault, fuse_cfg_t* cfg); - -#ifdef TESTING -#endif - -#endif /* FUSES_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/board/utils/utils.c b/firmware/src/hw/board/utils/utils.c deleted file mode 100644 index e1f673d..0000000 --- a/firmware/src/hw/board/utils/utils.c +++ /dev/null @@ -1,272 +0,0 @@ -/**** Includes ****/ -#include "utils.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -#ifndef TESTING -static uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis); -static uint16_t interpolate_u16b(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1); -static uint16_t index2d_to_index1d(uint8_t ix, uint8_t iy, uint8_t len_x); -#endif - -/**** Public function definitions ****/ -uint8_t util_invert_8b(uint8_t x) -{ - if(x!=0) return 0; - else return 1; -} - -uint8_t util_sat_add_8b(uint8_t x, uint8_t y) -{ - uint8_t z = x + y; - // Check for overflow - if((z < x)||(z < y)) return 0xFF; - else return z; -} - -uint8_t util_sat_subtract_8b(uint8_t x, uint8_t y) -{ - uint8_t z = x - y; - // Check for underflow - if(z > x) return 0; - else return z; -} - -uint16_t util_sat_add_16b(uint16_t x, uint16_t y) -{ - uint16_t z = x + y; - // Check for overflow - if((z < x)||(z < y)) return 0xFFFF; - else return z; -} - -uint16_t util_sat_subtract_16b(uint16_t x, uint16_t y) -{ - uint16_t z = x - y; - // Check for underflow - if(z > x) return 0; - else return z; -} - -uint16_t util_limit_u32b_to_u16b(uint32_t in) -{ - if(in == 0) return 0; - else if(in >= 0x0000FFFF) return 0xFFFF; - else return (uint16_t)in; -} - -uint16_t util_limit_s32b_to_u16b(int32_t in) -{ - if(in <= 0) return 0; - else if(in >= 0x0000FFFF) return 0xFFFF; - else return (uint16_t)in; -} - -uint16_t util_convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset) -{ - int32_t temp = (int32_t)raw; - - temp = temp * mul; - if(div>1) temp /= div; - temp += offset; - - return util_limit_s32b_to_u16b(temp); -} - -uint16_t util_sat_mul_kilo(uint16_t xk, uint16_t yk) -{ - uint32_t temp = (uint32_t)xk * (uint32_t)yk; - temp /= 1000; - - return util_limit_u32b_to_u16b(temp); -} - -uint16_t util_sat_div_kilo(uint16_t top, uint16_t bot) -{ - //Sanity check bot - if(bot==0) return 0xFFFF; //aka infinity - - uint32_t temp = (uint32_t)top * 1000; - temp /= (uint32_t)bot; - - return util_limit_u32b_to_u16b(temp); -} - -uint16_t util_sat_ratio_16b(uint16_t top, uint16_t bot) -{ - //Sanity check bot - if(bot==0) return 0xFFFF; //aka infinity - - //Easy option - if(top>=bot) return 0xFFFF; - - uint32_t temp = (uint32_t)top * 0x0000FFFF; - temp /= (uint32_t)bot; - - return util_limit_u32b_to_u16b(temp); -} - -uint16_t util_percent_to_16b(uint8_t percent) -{ - uint32_t temp = (uint32_t)percent * 0x0000FFFF; - temp /= 100; - - // Limit to 16 bits - uint16_t pwm = util_limit_u32b_to_u16b(temp); - - return pwm; -} - -uint16_t util_interpolate_1d_u16b(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis) -{ - // validate axis length - if(len_axis==0) return 0; // Empty data set - if(len_axis==1) return y_values[0]; // Only one data point - - uint16_t y; - - uint8_t i = find_interval_end_index(x, x_axis, len_axis); - if(i==0) - { - //Less then start - y = y_values[0]; - } - else if(i==len_axis) - { - //More than end - y = y_values[len_axis-1]; - } - else - { - // Do interpolate - y = interpolate_u16b(x, x_axis[i-1], x_axis[i], y_values[i-1], y_values[i]); - } - - return y; -} - -uint16_t util_interpolate_2d_u16b(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values) -{ - // validate axis length - if((len_x_axis==0)&&(len_y_axis==0)) return 0; // Empty data set - if((len_x_axis==1)&&(len_y_axis==1)) return z_values[0]; // Only one data point - - uint8_t ix = find_interval_end_index(x, x_axis, len_x_axis); - uint8_t iy = find_interval_end_index(y, y_axis, len_y_axis); - - // Check corners - easy answers - if((ix==0)&&(iy==0)) - { - return z_values[0]; //[0][0] [Y][X] - } - else if((ix==len_x_axis)&&(iy==0)) - { - return z_values[len_x_axis-1]; //[0][end] - } - else if((ix==0)&&(iy==len_y_axis)) - { - uint16_t i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); - return z_values[i]; //[end][0] - } - else if((ix==len_x_axis)&&(iy==len_y_axis)) - { - uint16_t i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); - return z_values[i]; //[end][end] - }; - - // Check boundaries - 1D interpolation - if(ix==0) - { - // On ix=0 line - uint16_t i = 0; - uint16_t z0 = z_values[i]; - i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); - uint16_t z1 = z_values[i]; - return interpolate_u16b(y, y_axis[0], y_axis[len_y_axis-1], z0, z1); - } - else if(ix==len_x_axis) - { - // On ix=END line - uint16_t i = len_x_axis-1; - uint16_t z0 = z_values[i]; - i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); - uint16_t z1 = z_values[i]; - return interpolate_u16b(y, y_axis[0], y_axis[len_y_axis-1], z0, z1); - } - else if(iy==0) - { - // On iy=0 line - uint16_t i = 0; - uint16_t z0 = z_values[i]; - i = len_x_axis-1; - uint16_t z1 = z_values[i]; - return interpolate_u16b(x, x_axis[0], x_axis[len_x_axis-1], z0, z1); - } - else if(iy==len_y_axis) - { - // On iy=END line - uint16_t i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); - uint16_t z0 = z_values[i]; - i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); - uint16_t z1 = z_values[i]; - return interpolate_u16b(x, x_axis[0], x_axis[len_x_axis-1], z0, z1); - } - - // Do interpolation - // Get axis values - uint16_t x0 = x_axis[ix-1]; - uint16_t x1 = x_axis[ix]; - uint16_t y0 = y_axis[iy-1]; - uint16_t y1 = y_axis[iy]; - - // Do y0 line calculation - // Get z values at x0 and x1 points on y0 line - uint16_t i = index2d_to_index1d(ix-1, iy-1, len_x_axis); - uint16_t z0 = z_values[i]; - uint16_t z1 = z_values[i+1]; - // Interpolate z value on y0 line - uint16_t zy0 = interpolate_u16b(x, x0, x1, z0, z1); - - // Do y1 line calculation - // Get z values at x0 and x1 points on y1 line - i = index2d_to_index1d(ix-1, iy, len_x_axis); - z0 = z_values[i]; - z1 = z_values[i+1]; - // Interpolate z value on y0 line - uint16_t zy1 = interpolate_u16b(x, x0, x1, z0, z1); - - // Do calculation in y axis on xz line - return interpolate_u16b(y, y0, y1, zy0, zy1); -} - -/**** Private function definitions ****/ -static uint16_t interpolate_u16b(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1) -{ - int32_t dy = (int32_t)y1 - (int32_t)y0; - int32_t dx = (int32_t)x1 - (int32_t)x0; - int32_t d = (int32_t)x - (int32_t)x0; - - int32_t y = dy * d; - y /= dx; - y += y0; - - return util_limit_s32b_to_u16b(y); -} - -static uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis) -{ - for(uint8_t i=0; i - -/**** Public definitions ****/ - -/**** Public function declarations ****/ -uint8_t util_invert_8b(uint8_t x); - -uint16_t util_limit_u32b_to_u16b(uint32_t in); -uint16_t util_limit_s32b_to_u16b(int32_t in); - -uint16_t util_convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset); -uint16_t util_sat_mul_kilo(uint16_t xk, uint16_t yk); -uint16_t util_sat_div_kilo(uint16_t top, uint16_t bot); -uint16_t util_sat_ratio_16b(uint16_t top, uint16_t bot); -uint16_t util_percent_to_16b(uint8_t percent); - -uint8_t util_sat_add_8b(uint8_t x, uint8_t y); -uint8_t util_sat_subtract_8b(uint8_t x, uint8_t y); - -uint16_t util_sat_add_16b(uint16_t x, uint16_t y); -uint16_t util_sat_subtract_16b(uint16_t x, uint16_t y); - -uint16_t util_interpolate_1d_u16b(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis); -uint16_t util_interpolate_2d_u16b(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values); - -#ifdef TESTING -// Access to private functions for unit testing -static uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis); -static uint16_t interpolate_u16b(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1); -static uint16_t index2d_to_index1d(uint8_t ix, uint8_t iy, uint8_t len_x); -#endif - -#endif /* UTILS_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/buttons.c b/firmware/src/hw/buttons.c deleted file mode 100644 index b8c60d5..0000000 --- a/firmware/src/hw/buttons.c +++ /dev/null @@ -1,173 +0,0 @@ -/**** Includes ****/ -#include "board/utils/utils.h" -#include "board/din.h" -#include "board/dout.h" -#include "buttons.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint8_t DEF_DBNC_LIM = HW_BTN_DEF_DBNC_LIM; -static const uint16_t DEF_REPEAT_TIME = HW_BTN_DEF_REPEAT_TIME; - -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t buttons_mapping(uint8_t btn_ch, uint8_t* din_ch, btn_cfg_t* cfg); -static uint8_t buttons_pull_mapping(uint8_t btn_ch, uint8_t* dout_ch); - -/**** Public function definitions ****/ -void btn_reset(btn_t* btn) -{ - btn->state = BTN_INACTIVE; - btn->new_state = 0; - btn->state_time = 0; - btn->transition_cntr = 0; - btn->dbnc_active = 1; -} - -uint8_t btn_ch_process(uint8_t btn_ch, btn_t* btn) -{ - // Get channel config - uint8_t din_ch; - btn_cfg_t cfg; - if(buttons_mapping(btn_ch, &din_ch, &cfg)) return BTN_INACTIVE; - - // Read din level - uint8_t lvl = bsp_din_read(din_ch); - - // Process button logic - uint8_t btn_act = btn_process(lvl, btn, &cfg); - return btn_act; -} - -uint8_t btn_process(uint8_t lvl, btn_t* btn, btn_cfg_t* cfg) -{ - // Increase state time - btn->state_time = util_sat_add_16b(btn->state_time, 1); - - // Check repeated new flag - if((cfg->repeat_time)&&(btn->state_time < 0xFFFF)) - { - if((btn->state_time)%(cfg->repeat_time) == 0) btn->new_state = 1; - }; - - // Calculate saved level from state and active level cfg - uint8_t prev_lvl = 0; - if(((btn->dbnc_active)&&(btn->state))||((!btn->dbnc_active)&&(!btn->state))) prev_lvl = util_invert_8b(cfg->act_lvl); - else prev_lvl = cfg->act_lvl; - - // Check if level changed - uint8_t lvl_chnaged = 0; - if(lvl!=prev_lvl) lvl_chnaged = 1; - - //Process debounce logic - if(lvl_chnaged) - { - // Changed debounce state - if(!btn->dbnc_active) btn->dbnc_active = 1; // Start debounce - else btn->dbnc_active = 0; // Stop debounce - // Reset debounce counter - btn->transition_cntr = 0; - } - else - { - // Continue debounce - if(btn->dbnc_active) btn->transition_cntr += 1; - } - - // Check for debounce end - if((btn->dbnc_active)&&(btn->transition_cntr >= cfg->dbnc_lim)) - { - // Reset debounce - btn->dbnc_active = 0; //End debounce - btn->transition_cntr = 0; - // Set new state - if(lvl==cfg->act_lvl) btn->state = BTN_ACTIVE; - else btn->state = BTN_INACTIVE; - btn->state_time = 0; //Reset timer - btn->new_state = 1; //Set new flag - }; - - return btn->state; -} - -void btn_ch_set_pull(uint8_t btn_ch, int8_t lvl) -{ - // Get channel config - uint8_t dout_ch; - if(buttons_pull_mapping(btn_ch, &dout_ch)) return; - - // Set button pull - bsp_dout_write(dout_ch, lvl); -} - -/**** Private function definitions ****/ -static uint8_t buttons_mapping(uint8_t btn_ch, uint8_t* din_ch, btn_cfg_t* cfg) -{ - // Default, most common config - cfg->act_lvl = BSP_DIN_LOW; - cfg->dbnc_lim = DEF_DBNC_LIM; - cfg->repeat_time = DEF_REPEAT_TIME; - - switch(btn_ch) - { - case BTN_1: // Mode - *din_ch = BSP_DIN1; - return 0; - - case BTN_2: // Down - *din_ch = BSP_DIN3; - return 0; - - case BTN_3: // Up - *din_ch = BSP_DIN4; - return 0; - - case BTN_4: // Dimm - *din_ch = BSP_DIN5; - cfg->act_lvl = BSP_DIN_HIGH; - cfg->repeat_time = 0; - return 0; - - case BTN_5: // Brakes - *din_ch = BSP_DIN6; - cfg->act_lvl = BSP_DIN_HIGH; - cfg->repeat_time = 0; - return 0; - - case BTN_6: // Handbrake - *din_ch = BSP_DIN7; - cfg->repeat_time = 0; - return 0; - - case BTN_6N: // Handbrake inverted - *din_ch = BSP_DIN7N; - cfg->repeat_time = 0; - return 0; - - default: //Invalid channel - *din_ch = 0; - return 1; - } - - return 1; -} - -static uint8_t buttons_pull_mapping(uint8_t btn_ch, uint8_t* dout_ch) -{ - switch(btn_ch) - { - case BTN_6: // Handbrake - *dout_ch = BSP_DOUT5; - return 0; - - case BTN_6N: // Handbrake inverted - *dout_ch = BSP_DOUT5; - return 0; - - default: //Invalid channel - *dout_ch = 0; - return 1; - } - - return 1; -} diff --git a/firmware/src/hw/buttons.h b/firmware/src/hw/buttons.h deleted file mode 100644 index 323406e..0000000 --- a/firmware/src/hw/buttons.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef BUTTONS_H_ -#define BUTTONS_H_ - -/* -*/ - -/**** Includes ****/ -#include -#include "config.h" - -/**** Public definitions ****/ -typedef struct { - uint8_t act_lvl; - uint8_t dbnc_lim; - uint16_t repeat_time; -} btn_cfg_t; - -typedef struct { - uint8_t state; - uint8_t new_state; - uint16_t state_time; - uint8_t transition_cntr; - uint8_t dbnc_active; -} btn_t; - -#define BTN_1 1 //DIN1 Mode -#define BTN_2 2 //DIN3 Down -#define BTN_3 3 //DIN4 Up -#define BTN_4 4 //DIN5 Dimm -#define BTN_5 5 //DIN6 Brakes -#define BTN_6 6 //DIN7 Handbrake -#define BTN_6N 7 //DIN7N Direct handbrake - -#define BTN_INACTIVE 0 -#define BTN_ACTIVE 1 - -#define BTN_PULL_LOW 0 -#define BTN_PULL_HIGH 1 -#define BTN_PULL_NONE -1 - -/**** Public function declarations ****/ -void btn_reset(btn_t* btn); - -// Using internal map -uint8_t btn_ch_process(uint8_t btn_ch, btn_t* btn); -void btn_ch_set_pull(uint8_t btn_ch, int8_t lvl); - -// Manual process -uint8_t btn_process(uint8_t lvl, btn_t* btn, btn_cfg_t* cfg); - -#ifdef TESTING -#endif - -#endif /* BUTTONS_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/config.h b/firmware/src/hw/config.h deleted file mode 100644 index ac63781..0000000 --- a/firmware/src/hw/config.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef HW_CONFIG_H_ -#define HW_CONFIG_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define HW_BTN_DEF_DBNC_LIM 10 -#define HW_BTN_DEF_REPEAT_TIME 1000 - -#define HW_HB_SUPPLY_VOLT_MIN_W 10000 -#define HW_HB_SUPPLY_VOLT_MIN_F 8000 - -#define HW_HB_SUPPLY_VOLT_MAX_W 16000 -#define HW_HB_SUPPLY_VOLT_MAX_F 19500 - -#define HW_HB_SUPPLY_CURRENT_MAX_W 6000 -#define HW_HB_SUPPLY_CURRENT_MAX_F 8000 - -#define HW_HB_SUPPLY_POWER_MAX_W 30000 -#define HW_HB_SUPPLY_POWER_MAX_F 45000 - -#define HW_HB_OUT_CURRENT_MAX_W 6000 -#define HW_HB_OUT_CURRENT_MAX_F 8000 - -#define HW_HB_OUT_VOLTAGE_MAX_W 8000 -#define HW_HB_OUT_VOLTAGE_MAX_F 10000 - -#define HW_HB_OUT_POWER_MAX_W 30000 -#define HW_HB_OUT_POWER_MAX_F 40000 - -#define HW_HB_OUT_RESISTANCE_MIN_W 750 -#define HW_HB_OUT_RESISTANCE_MIN_F 500 - -#define HW_HB_OUT_RESISTANCE_MAX_W 5000 -#define HW_HB_OUT_RESISTANCE_MAX_F 10000 - -#define HW_HB_FAULT_DELAY 500 -#define HW_HB_WTOF_DELAY 0 - -/**** Public function declarations ****/ -#ifdef TESTING -#endif - -#endif /* HW_CONFIG_H_ */ diff --git a/firmware/src/hw/hb_control.c b/firmware/src/hw/hb_control.c deleted file mode 100644 index 3ab05d6..0000000 --- a/firmware/src/hw/hb_control.c +++ /dev/null @@ -1,399 +0,0 @@ -/**** Includes ****/ -#include "board/utils/utils.h" -#include "board/halfbridge.h" -#include "hb_control.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -// Fault condition thresholds -static const uint16_t SUPPLY_VOLT_MIN_W = HW_HB_SUPPLY_VOLT_MIN_W; -static const uint16_t SUPPLY_VOLT_MIN_F = HW_HB_SUPPLY_VOLT_MIN_F; - -static const uint16_t SUPPLY_VOLT_MAX_W = HW_HB_SUPPLY_VOLT_MAX_W; -static const uint16_t SUPPLY_VOLT_MAX_F = HW_HB_SUPPLY_VOLT_MAX_F; - -static const uint16_t SUPPLY_CURRENT_MAX_W = HW_HB_SUPPLY_CURRENT_MAX_W; -static const uint16_t SUPPLY_CURRENT_MAX_F = HW_HB_SUPPLY_CURRENT_MAX_F; - -static const uint16_t SUPPLY_POWER_MAX_W = HW_HB_SUPPLY_POWER_MAX_W; -static const uint16_t SUPPLY_POWER_MAX_F = HW_HB_SUPPLY_POWER_MAX_F; - -static const uint16_t OUT_CURRENT_MAX_W = HW_HB_OUT_CURRENT_MAX_W; -static const uint16_t OUT_CURRENT_MAX_F = HW_HB_OUT_CURRENT_MAX_F; - -static const uint16_t OUT_VOLTAGE_MAX_W = HW_HB_OUT_VOLTAGE_MAX_W; -static const uint16_t OUT_VOLTAGE_MAX_F = HW_HB_OUT_VOLTAGE_MAX_F; - -static const uint16_t OUT_POWER_MAX_W = HW_HB_OUT_POWER_MAX_W; -static const uint16_t OUT_POWER_MAX_F = HW_HB_OUT_POWER_MAX_F; - -static const uint16_t OUT_RESISTANCE_MIN_W = HW_HB_OUT_RESISTANCE_MIN_W; -static const uint16_t OUT_RESISTANCE_MIN_F = HW_HB_OUT_RESISTANCE_MIN_F; - -static const uint16_t OUT_RESISTANCE_MAX_W = HW_HB_OUT_RESISTANCE_MAX_W; -static const uint16_t OUT_RESISTANCE_MAX_F = HW_HB_OUT_RESISTANCE_MAX_F; - -static const uint16_t FAULT_DELAY = HW_HB_FAULT_DELAY; -static const uint16_t WTOF_DELAY = HW_HB_WTOF_DELAY; - -/**** Private variables ****/ - -/**** Private function declarations ****/ -// Protection functions -static void faults_chs_reset(hb_fault_chs_t* ch_list); -static void faults_init(hb_faults_t* faults, hb_fault_chs_t* en_list); -static void faults_logic(hb_meas_t* measurements, hb_faults_t* faults); - -static uint8_t faults_check(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list, fault_lvl_t check); -static uint8_t warnings_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list); -static uint8_t faults_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list); -static uint8_t faults_is_any(hb_fault_chs_t* fb_list); - -static uint8_t is_outside(uint16_t meas, uint16_t min, uint16_t max); - -static uint8_t fuse_subprocess(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl); -static void target_to_control(int16_t target, uint16_t ref_voltage, uint16_t* pwm_out, uint8_t* low_out, hb_fault_chs_t* fault_ch_en); - -/**** Public function definitions ****/ -void hb_init(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl) -{ - // Initialize control - hb_ctrl->enabled = 0; - - // Initialize protection structures - faults_init(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en); - - fuse_reset(&hb_ctrl->out_fuse); - hb_ctrl->out_fuse_cfg.cooldown_time = 1000; - hb_ctrl->out_fuse_cfg.retry_time = 1000; - - // Initialize feedback structure - bsp_hb_read_meas(&hb_fb->analog); - faults_chs_reset(&hb_fb->faults); - faults_chs_reset(&hb_fb->warnings); - - hb_fb->enabled = 0; - hb_fb->warning_act = 0; - hb_fb->fault_act = 0; - hb_fb->fused = 0; - - bsp_hb_write_pwm(0); - bsp_hb_write_low(0); -} - -void hb_enable(hb_control_t* hb_ctrl) -{ - bsp_hb_write_pwm(0); - bsp_hb_write_low(1); - hb_ctrl->enabled = 1; -} - -void hb_disable(hb_control_t* hb_ctrl) -{ - hb_ctrl->enabled = 0; - bsp_hb_write_pwm(0); - bsp_hb_write_low(0); -} - - -void hb_process(int16_t target, hb_feedback_t* hb_fb, hb_control_t* hb_ctrl) -{ - uint16_t out_pwm = 0; - uint8_t low_on = 0; - - // Read feedback - bsp_hb_read_meas(&hb_fb->analog); - - // Process fuse - uint8_t fuse_act = fuse_subprocess(hb_fb, hb_ctrl); - - // Act on fuse state - if((fuse_act)||(!hb_ctrl->enabled)) - { - // Turn off output - bsp_hb_write_pwm(0); - bsp_hb_write_low(0); - return; - }; - - // Process new target - target_to_control(target, hb_fb->analog.sup_voltage, &out_pwm, &low_on, &hb_ctrl->out_faults_en); - - // Apply new controls - bsp_hb_write_low(low_on); - bsp_hb_write_pwm(out_pwm); -} - -/**** Private function definitions ****/ -static void target_to_control(int16_t target, uint16_t ref_voltage, uint16_t* pwm_out, uint8_t* low_out, hb_fault_chs_t* fault_ch_en) -{ - if(target < 0) - { - // Fast decay - *pwm_out = 0; - *low_out = 0; - // Set appropriate fault channels - fault_ch_en->out_short = 0; - fault_ch_en->out_ovp = 0; - } - else if(target == 0) - { - // Slow decay - *pwm_out = 0; - *low_out = 1; - // Set appropriate fault channels - fault_ch_en->out_short = 0; - fault_ch_en->out_ovp = 1; - } - else - { - // Calculate target PWM - *pwm_out = util_sat_ratio_16b((uint16_t)target, ref_voltage); - *low_out = 1; - fault_ch_en->out_short = 1; - fault_ch_en->out_ovp = 1; - } -} - -static uint8_t fuse_subprocess(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl) -{ - // Process faults - faults_logic(&hb_fb->analog, &hb_ctrl->out_faults); - - // Check if any enabled fault is active - uint8_t warn_act = warnings_feedback(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en, &hb_fb->warnings); - uint8_t fault_act = faults_feedback(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en, &hb_fb->faults); - - // Process fuse state - uint8_t fuse_act = fuse_process(&hb_ctrl->out_fuse, fault_act, &hb_ctrl->out_fuse_cfg); - - // Copy feedback data - hb_fb->enabled = hb_ctrl->enabled; - hb_fb->warning_act = warn_act; - hb_fb->fault_act = fault_act; - hb_fb->fused = fuse_act; - - return fuse_act; -} - -// Fault logic functions -static void faults_chs_reset(hb_fault_chs_t* ch_list) -{ - // Zero all channels - ch_list->sup_uvp = 0; - ch_list->sup_ovp = 0; - ch_list->sup_ocp = 0; - ch_list->sup_opp = 0; - ch_list->out_ovp = 0; - ch_list->out_ocp = 0; - ch_list->out_opp = 0; - ch_list->out_short = 0; - ch_list->out_open = 0; -} - -static void faults_init(hb_faults_t* faults, hb_fault_chs_t* en_list) -{ - // Init state - fault_reset(&faults->sup_uvp); - fault_reset(&faults->sup_ovp); - fault_reset(&faults->sup_ocp); - fault_reset(&faults->sup_opp); - fault_reset(&faults->out_ovp); - fault_reset(&faults->out_ocp); - fault_reset(&faults->out_opp); - fault_reset(&faults->out_short); - fault_reset(&faults->out_open); - - // Init enabled channels - faults_chs_reset(en_list); - en_list->sup_ocp = 1; - en_list->out_ocp = 1; - en_list->out_short = 1; -} - -static void faults_logic(hb_meas_t* measurements, hb_faults_t* faults) -{ - uint8_t w_trig = 0; - uint8_t f_trig = 0; - - fault_cfg_t fault_cfg; - fault_cfg.delay = FAULT_DELAY; - fault_cfg.wtof = WTOF_DELAY; - - // Check supply voltage - w_trig = is_outside(measurements->sup_voltage, SUPPLY_VOLT_MIN_W, 0); - f_trig = is_outside(measurements->sup_voltage, SUPPLY_VOLT_MIN_F, 0); - fault_process(&faults->sup_uvp, w_trig, f_trig, &fault_cfg); - - w_trig = is_outside(measurements->sup_voltage, 0, SUPPLY_VOLT_MAX_W); - f_trig = is_outside(measurements->sup_voltage, 0, SUPPLY_VOLT_MAX_F); - fault_process(&faults->sup_ovp, w_trig, f_trig, &fault_cfg); - - // Check supply current - w_trig = is_outside(measurements->sup_current, 0, SUPPLY_CURRENT_MAX_W); - f_trig = is_outside(measurements->sup_current, 0, SUPPLY_CURRENT_MAX_F); - fault_process(&faults->sup_ocp, w_trig, f_trig, &fault_cfg); - - // Check supply power - w_trig = is_outside(measurements->sup_power, 0, SUPPLY_POWER_MAX_W); - f_trig = is_outside(measurements->sup_power, 0, SUPPLY_POWER_MAX_F); - fault_process(&faults->sup_opp, w_trig, f_trig, &fault_cfg); - - // Check output voltage - w_trig = is_outside(measurements->out_voltage, 0, OUT_VOLTAGE_MAX_W); - f_trig = is_outside(measurements->out_voltage, 0, OUT_VOLTAGE_MAX_F); - fault_process(&faults->out_ovp, w_trig, f_trig, &fault_cfg); - - // Check output current - w_trig = is_outside(measurements->out_current, 0, OUT_CURRENT_MAX_W); - f_trig = is_outside(measurements->out_current, 0, OUT_CURRENT_MAX_F); - fault_process(&faults->out_ocp, w_trig, f_trig, &fault_cfg); - - // Check output power - w_trig = is_outside(measurements->out_power, 0, OUT_POWER_MAX_W); - f_trig = is_outside(measurements->out_power, 0, OUT_POWER_MAX_F); - fault_process(&faults->out_opp, w_trig, f_trig, &fault_cfg); - - // Check output resistance - w_trig = is_outside(measurements->out_impedance, OUT_RESISTANCE_MIN_W, 0); - f_trig = is_outside(measurements->out_impedance, OUT_RESISTANCE_MIN_F, 0); - fault_process(&faults->out_short, w_trig, f_trig, &fault_cfg); - - w_trig = is_outside(measurements->out_impedance, 0, OUT_RESISTANCE_MAX_W); - f_trig = is_outside(measurements->out_impedance, 0, OUT_RESISTANCE_MAX_F); - fault_process(&faults->out_open, w_trig, f_trig, &fault_cfg); -} - -static uint8_t warnings_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list) -{ - return faults_check(faults, en_list, fb_list, FAULT_LVL_WARNING); -} - -static uint8_t faults_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list) -{ - return faults_check(faults, en_list, fb_list, FAULT_LVL_FAULT); -} - -// Utilities -static uint8_t is_outside(uint16_t meas, uint16_t min, uint16_t max) -{ - if((meas < min)||((max!=0)&&(meas > max))) return 1; - else return 0; -} - -static uint8_t faults_check(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list, fault_lvl_t check) -{ - if((en_list->sup_uvp)&&(faults->sup_uvp.severity == check)) fb_list->sup_uvp = 1; - if((en_list->sup_ovp)&&(faults->sup_ovp.severity == check)) fb_list->sup_ovp = 1; - if((en_list->sup_ocp)&&(faults->sup_ocp.severity == check)) fb_list->sup_ocp = 1; - if((en_list->sup_opp)&&(faults->sup_opp.severity == check)) fb_list->sup_opp = 1; - if((en_list->out_ovp)&&(faults->out_ovp.severity == check)) fb_list->out_ovp = 1; - if((en_list->out_ocp)&&(faults->out_ocp.severity == check)) fb_list->out_ocp = 1; - if((en_list->out_opp)&&(faults->out_opp.severity == check)) fb_list->out_opp = 1; - if((en_list->out_short)&&(faults->out_short.severity == check)) fb_list->out_short = 1; - if((en_list->out_open)&&(faults->out_open.severity == check)) fb_list->out_open = 1; - - return faults_is_any(fb_list); -} - -static uint8_t faults_is_any(hb_fault_chs_t* fb_list) -{ - if(fb_list->sup_uvp) return 1; - if(fb_list->sup_ovp) return 1; - if(fb_list->sup_ocp) return 1; - if(fb_list->sup_opp) return 1; - if(fb_list->out_ovp) return 1; - if(fb_list->out_ocp) return 1; - if(fb_list->out_opp) return 1; - if(fb_list->out_short) return 1; - if(fb_list->out_open) return 1; - - return 0; -} - -#ifdef TESTING -uint8_t hb_is_equal_fb_struct(hb_feedback_t* f1, hb_feedback_t* f2) -{ - if(f1->analog.out_voltage != f2->analog.out_voltage ) return 0; - if(f1->analog.out_current != f2->analog.out_current ) return 0; - if(f1->analog.sup_voltage != f2->analog.sup_voltage ) return 0; - if(f1->analog.sup_current != f2->analog.sup_current ) return 0; - if(f1->analog.out_power != f2->analog.out_power ) return 0; - if(f1->analog.sup_power != f2->analog.sup_power ) return 0; - if(f1->analog.out_impedance != f2->analog.out_impedance ) return 0; - if(f1->analog.low_side_ctrl != f2->analog.low_side_ctrl ) return 0; - if(f1->analog.pwm != f2->analog.pwm ) return 0; - if(f1->enabled != f2->enabled ) return 0; - if(f1->warning_act != f2->warning_act ) return 0; - if(f1->fault_act != f2->fault_act ) return 0; - if(f1->fused != f2->fused ) return 0; - if(f1->warnings.sup_uvp != f2->warnings.sup_uvp ) return 0; - if(f1->warnings.sup_ovp != f2->warnings.sup_ovp ) return 0; - if(f1->warnings.sup_ocp != f2->warnings.sup_ocp ) return 0; - if(f1->warnings.sup_opp != f2->warnings.sup_opp ) return 0; - if(f1->warnings.out_ovp != f2->warnings.out_ovp ) return 0; - if(f1->warnings.out_ocp != f2->warnings.out_ocp ) return 0; - if(f1->warnings.out_opp != f2->warnings.out_opp ) return 0; - if(f1->warnings.out_short != f2->warnings.out_short ) return 0; - if(f1->warnings.out_open != f2->warnings.out_open ) return 0; - if(f1->faults.sup_uvp != f2->faults.sup_uvp ) return 0; - if(f1->faults.sup_ovp != f2->faults.sup_ovp ) return 0; - if(f1->faults.sup_ocp != f2->faults.sup_ocp ) return 0; - if(f1->faults.sup_opp != f2->faults.sup_opp ) return 0; - if(f1->faults.out_ovp != f2->faults.out_ovp ) return 0; - if(f1->faults.out_ocp != f2->faults.out_ocp ) return 0; - if(f1->faults.out_opp != f2->faults.out_opp ) return 0; - if(f1->faults.out_short != f2->faults.out_short ) return 0; - if(f1->faults.out_open != f2->faults.out_open ) return 0; - - return 1; -} - -uint8_t hb_is_equal_ctrl_struct(hb_control_t* c1, hb_control_t* c2) -{ - if(c1->enabled != c2->enabled ) return 0; - if(c1->out_faults.sup_uvp.severity != c2->out_faults.sup_uvp.severity ) return 0; - if(c1->out_faults.sup_uvp.w_time != c2->out_faults.sup_uvp.w_time ) return 0; - if(c1->out_faults.sup_uvp.f_time != c2->out_faults.sup_uvp.f_time ) return 0; - if(c1->out_faults.sup_ovp.severity != c2->out_faults.sup_ovp.severity ) return 0; - if(c1->out_faults.sup_ovp.w_time != c2->out_faults.sup_ovp.w_time ) return 0; - if(c1->out_faults.sup_ovp.f_time != c2->out_faults.sup_ovp.f_time ) return 0; - if(c1->out_faults.sup_ocp.severity != c2->out_faults.sup_ocp.severity ) return 0; - if(c1->out_faults.sup_ocp.w_time != c2->out_faults.sup_ocp.w_time ) return 0; - if(c1->out_faults.sup_ocp.f_time != c2->out_faults.sup_ocp.f_time ) return 0; - if(c1->out_faults.sup_opp.severity != c2->out_faults.sup_opp.severity ) return 0; - if(c1->out_faults.sup_opp.w_time != c2->out_faults.sup_opp.w_time ) return 0; - if(c1->out_faults.sup_opp.f_time != c2->out_faults.sup_opp.f_time ) return 0; - if(c1->out_faults.out_ovp.severity != c2->out_faults.out_ovp.severity ) return 0; - if(c1->out_faults.out_ovp.w_time != c2->out_faults.out_ovp.w_time ) return 0; - if(c1->out_faults.out_ovp.f_time != c2->out_faults.out_ovp.f_time ) return 0; - if(c1->out_faults.out_ocp.severity != c2->out_faults.out_ocp.severity ) return 0; - if(c1->out_faults.out_ocp.w_time != c2->out_faults.out_ocp.w_time ) return 0; - if(c1->out_faults.out_ocp.f_time != c2->out_faults.out_ocp.f_time ) return 0; - if(c1->out_faults.out_opp.severity != c2->out_faults.out_opp.severity ) return 0; - if(c1->out_faults.out_opp.w_time != c2->out_faults.out_opp.w_time ) return 0; - if(c1->out_faults.out_opp.f_time != c2->out_faults.out_opp.f_time ) return 0; - if(c1->out_faults.out_short.severity != c2->out_faults.out_short.severity ) return 0; - if(c1->out_faults.out_short.w_time != c2->out_faults.out_short.w_time ) return 0; - if(c1->out_faults.out_short.f_time != c2->out_faults.out_short.f_time ) return 0; - if(c1->out_faults.out_open.severity != c2->out_faults.out_open.severity ) return 0; - if(c1->out_faults.out_open.w_time != c2->out_faults.out_open.w_time ) return 0; - if(c1->out_faults.out_open.f_time != c2->out_faults.out_open.f_time ) return 0; - if(c1->out_faults_en.sup_uvp != c2->out_faults_en.sup_uvp ) return 0; - if(c1->out_faults_en.sup_ovp != c2->out_faults_en.sup_ovp ) return 0; - if(c1->out_faults_en.sup_ocp != c2->out_faults_en.sup_ocp ) return 0; - if(c1->out_faults_en.sup_opp != c2->out_faults_en.sup_opp ) return 0; - if(c1->out_faults_en.out_ovp != c2->out_faults_en.out_ovp ) return 0; - if(c1->out_faults_en.out_ocp != c2->out_faults_en.out_ocp ) return 0; - if(c1->out_faults_en.out_opp != c2->out_faults_en.out_opp ) return 0; - if(c1->out_faults_en.out_short != c2->out_faults_en.out_short ) return 0; - if(c1->out_faults_en.out_open != c2->out_faults_en.out_open ) return 0; - if(c1->out_fuse.state != c2->out_fuse.state ) return 0; - if(c1->out_fuse.count != c2->out_fuse.count ) return 0; - if(c1->out_fuse.timer != c2->out_fuse.timer ) return 0; - if(c1->out_fuse_cfg.cooldown_time != c2->out_fuse_cfg.cooldown_time ) return 0; - if(c1->out_fuse_cfg.retry_time != c2->out_fuse_cfg.retry_time ) return 0; - - return 1; -} -#endif diff --git a/firmware/src/hw/hb_control.h b/firmware/src/hw/hb_control.h deleted file mode 100644 index aae6afe..0000000 --- a/firmware/src/hw/hb_control.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef HB_CONTROL_H_ -#define HB_CONTROL_H_ - -/**** Includes ****/ -#include -#include "board/utils/faults.h" -#include "board/utils/fuses.h" -#include "board/halfbridge.h" -#include "config.h" - -/**** Public definitions ****/ -typedef struct { - fault_t sup_uvp; - fault_t sup_ovp; - fault_t sup_ocp; - fault_t sup_opp; - fault_t out_ovp; - fault_t out_ocp; - fault_t out_opp; - fault_t out_short; - fault_t out_open; -} hb_faults_t; - -typedef struct { - uint8_t sup_uvp; - uint8_t sup_ovp; - uint8_t sup_ocp; - uint8_t sup_opp; - uint8_t out_ovp; - uint8_t out_ocp; - uint8_t out_opp; - uint8_t out_short; - uint8_t out_open; -} hb_fault_chs_t; - -typedef struct { - hb_meas_t analog; - uint8_t enabled; - uint8_t warning_act; - uint8_t fault_act; - uint8_t fused; - hb_fault_chs_t warnings; - hb_fault_chs_t faults; -} hb_feedback_t; - -typedef struct { - uint8_t enabled; - hb_faults_t out_faults; - hb_fault_chs_t out_faults_en; - fuse_t out_fuse; - fuse_cfg_t out_fuse_cfg; -} hb_control_t; - -/**** Public function declarations ****/ -void hb_init(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl); - -void hb_enable(hb_control_t* hb_ctrl); -void hb_disable(hb_control_t* hb_ctrl); - -void hb_process(int16_t target, hb_feedback_t* hb_fb, hb_control_t* hb_ctrl); - -#ifdef TESTING -uint8_t hb_is_equal_fb_struct(hb_feedback_t* f1, hb_feedback_t* f2); -uint8_t hb_is_equal_ctrl_struct(hb_control_t* c1, hb_control_t* c2); -#endif - -#endif /* HB_CONTROL_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/led_display.c b/firmware/src/hw/led_display.c deleted file mode 100644 index d3ebc89..0000000 --- a/firmware/src/hw/led_display.c +++ /dev/null @@ -1,38 +0,0 @@ -/**** Includes ****/ -#include "board/odout.h" -#include "led_display.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -void led_dsp_set_image(uint8_t image) -{ - if(image&0x01) bsp_odout_write(BSP_OD1, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD1, BSP_ODOUT_HIZ); - - if(image&0x02) bsp_odout_write(BSP_OD2, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD2, BSP_ODOUT_HIZ); - - if(image&0x04) bsp_odout_write(BSP_OD3, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD3, BSP_ODOUT_HIZ); - - if(image&0x08) bsp_odout_write(BSP_OD4, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD4, BSP_ODOUT_HIZ); - - if(image&0x10) bsp_odout_write(BSP_OD5, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD5, BSP_ODOUT_HIZ); - - if(image&0x20) bsp_odout_write(BSP_OD6, BSP_ODOUT_LOW); - else bsp_odout_write(BSP_OD6, BSP_ODOUT_HIZ); -} - -void led_dsp_backligth_set(uint8_t percent) -{ - bsp_odout_write_common(percent); -} - -/**** Private function definitions ****/ - diff --git a/firmware/src/hw/led_display.h b/firmware/src/hw/led_display.h deleted file mode 100644 index ac61a08..0000000 --- a/firmware/src/hw/led_display.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LED_DISPLAY_H_ -#define LED_DISPLAY_H_ - -/* -*/ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ - -/**** Public function declarations ****/ -void led_dsp_set_image(uint8_t image); -void led_dsp_backligth_set(uint8_t percent); - -#ifdef TESTING -#endif - -#endif /* LED_DISPLAY_H_ */ diff --git a/firmware/src/hw/mcu/mcu_hal.h b/firmware/src/hw/mcu/mcu_hal.h new file mode 100644 index 0000000..468975b --- /dev/null +++ b/firmware/src/hw/mcu/mcu_hal.h @@ -0,0 +1,102 @@ +#ifndef UDCCD_R8_BSP_H_ +#define UDCCD_R8_BSP_H_ + +/**** Includes ****/ +#include +#include "../common/level.h" + +/**** Public definitions ****/ +//ADC definitions +typedef enum { + ADC_ICOIL = 0x00, + ADC_UCOIL = 0x01, + ADC_UBAT = 0x02, + ADC_IBAT = 0x03, + ADC_POT = 0x04, + ADC_MODE = 0x05, + ADC_TEMP = 0x08, + ADC_INTREF = 0x0E, + ADC_GND = 0x0F +} adcCh_t; + +typedef enum { + ADC_DIV2 = 0x01, + ADC_DIV4 = 0x02, + ADC_DIV8 = 0x03, + ADC_DIV16 = 0x04, + ADC_DIV32 = 0x05, + ADC_DIV64 = 0x06, + ADC_DIV128 = 0x07 +} adcDiv_t; + +//Timer definitions +typedef enum { + TIM_DIV1 = 0x01, + TIM_DIV8 = 0x02, + TIM_DIV64 = 0x03, + TIM_DIV256 = 0x04, + TIM_DIV1024 = 0x05 +} timerDiv_t; + +typedef enum { + PWM_COIL = 'A', + PWM_LED = 'B' +} pwmCh_t; + +typedef enum { + SPEED_1 = 3, + SPEED_0 = 4 +} speedCh_t; + +typedef struct { + adcDiv_t adc_clk_prescaler; + uint8_t adc_auto_wake; + timerDiv_t pwm_timer_prescaler; + uint16_t pwm_timer_top; + timerDiv_t freq_timer_prescaler; + uint16_t uart_prescaler; + uint8_t systick_timer_top; + timerDiv_t systick_timer_prescaler; + uint8_t disable_unused; + uint8_t en_watchdog; +} hwConfig_t; + +/**** Public function declarations ****/ +void HAL_Init_Min(hwConfig_t* hwCfg); +void HAL_Init_Extra(hwConfig_t* hwCfg); + +level_t HAL_ReadLvl_Handbrake(void); +level_t HAL_ReadLvl_Brake(void); +level_t HAL_ReadLvl_Dimm(void); +level_t HAL_ReadLvl_BtnUp(void); +level_t HAL_ReadLvl_BtnDown(void); +level_t HAL_ReadLvl_BtnMode(void); +level_t HAL_ReadLvl_HandbrakePull(void); +level_t HAL_ReadLvl_CoilLow(void); +level_t HAL_ReadLvl_CoilHigh(void); +level_t HAL_ReadLvl_LedsPwm(void); +level_t HAL_ReadLvl_Speed0(void); +level_t HAL_ReadLvl_Speed1(void); +level_t HAL_ReadLvl_SpeedPull(void); + +void HAL_SetPull_Handbrake(level_t lvl); +void HAL_SetPull_Speed(level_t lvl); + +void HAL_ADC_Wake(void); +void HAL_ADC_Sleep(void); +uint16_t HAL_ADC_Read(adcCh_t ch); + +void HAL_Coil_SetLowSide(uint8_t on); +void HAL_Coil_SetPWM(uint8_t percent); +void HAL_Coil_SetPWM16b(uint16_t value); + +void HAL_LEDS_Set(uint8_t image); +uint8_t HAL_LEDS_Get(void); +void HAL_LEDS_SetPWM(uint8_t percent); + +void HAL_PWM_Wake(void); +void HAL_PWM_Sleep(void); +void HAL_PWM_SetDuty16b(pwmCh_t ch, uint16_t value); +void HAL_PWM_SetDuty100(pwmCh_t ch, uint8_t percent); + +#endif /* UDCCD_R8_BSP_H_ */ diff --git a/firmware/src/hw/mcu/mcu_r8_hal.cpp b/firmware/src/hw/mcu/mcu_r8_hal.cpp new file mode 100644 index 0000000..fb28bcb --- /dev/null +++ b/firmware/src/hw/mcu/mcu_r8_hal.cpp @@ -0,0 +1,463 @@ +/**** Includes ****/ +#include +#include "udccd_hal.h" + +/**** Private variables ****/ +static uint8_t tim0_prescaler = 0x00; +static uint8_t tim1_prescaler = 0x00; +static uint8_t tim3_prescaler = 0x00; +static uint8_t tim4_prescaler = 0x00; + +/**** Private function declarations ****/ +static void PWM_SetOCx(pwmCh_t ch, uint16_t value); + +/**** Public function definitions ****/ +void HAL_Init_Min(hwConfig_t* hwCfg) +{ + // PIN Configuration + //DCCD Enable and PWM + PORTB &= ~0x03; //Set low + DDRB |= 0x03; //Set as outputs + + //LED PWM + PORTB &= ~0x04; //Set low + DDRB |= 0x04; //Set as output + + //UART TX + PORTB |= 0x18; //Set high / pull-up on + DDRB |= 0x08; //Set as output + DDRB &= ~0x10; //Set as input + + //Handbrake pull-up + PORTB |= 0x20; //Set high + DDRB |= 0x20; //Set as output + + //Handbrake and Brake inputs + PORTB |= 0xC0; //Pull-up on + DDRB &= ~0xC0; //Set as input + + // ADC inputs + PORTC &= ~0x3F; //Pull-up off + DDRC &= ~0x3F; //Set as inputs + + // Reset + PORTC |= 0x40; //Pull-up on + DDRC &= ~0x40; //Set as input + + // LED control + PORTD &= ~0x3F; //Set low + DDRD |= 0x3F; //Set as outputs + + // Speed pull + PORTD &= ~0x40; //Set low + DDRD |= 0x40; //Set as outputs + + // Dim input + PORTD |= 0x80; //Set pull-up on + DDRD &= ~0x80; //Set as input + + // Speed inputs + PORTE &= ~0x05; //Set pull-down + DDRE |= 0x05; //Set as output + + // Up/Down inputs + PORTE |= 0x0A; //Set pull-up on + DDRE &= ~0x0A; //Set as input + + + //ADC configuration + PRR0 &= ~0x01; //Enable ADC power + DIDR0 |= 0x1F; //Disable digital inputs, ADC0-ADC4 + + ADMUX = 0x40; //Set AVCC reference, Right adjust + ADCSRA = 0x00; //ADC Disabled, Single conversion, no IT + ADCSRA |= (uint8_t)hwCfg->adc_clk_prescaler; + ADCSRB = 0x00; //no trigger input + + if(hwCfg->adc_auto_wake) ADCSRA |= 0x80; //Enable ADC + else PRR0 |= 0x01; + + + //DCCD and LED PWM configuration + PRR0 &= ~0x80; //Enable Timer1 power + TCCR1A = 0xF2; //Connect OC1A and OC1B, normal logic + TCCR1B = 0x18; //PWM, Phase & Frequency Correct ICR1 top, no clock, WGM:0xE + TCCR1C = 0x00; + TCNT1 = 0x0000; + OCR1A = 0x0000; + OCR1B = 0x0000; + ICR1 = (hwCfg->pwm_timer_top); + TIMSK1 = 0x00; //No interrupts + TIFR1 = 0x00; //Clear all flags + + tim1_prescaler = (uint8_t)hwCfg->pwm_timer_prescaler; + TCCR1B |= tim1_prescaler; //Enable timer +} + +void HAL_Init_Extra(hwConfig_t* hwCfg) +{ + //Speed 1 input timer configuration + PRR1 &= ~0x01; //Enable Timer3 power + TCCR3A = 0x00; //OCx disconnected, WGM:0x0 + TCCR3B = 0x80; //ICP Noise filter, Falling edge, no clock + TCCR3C = 0x00; + TCNT3 = 0x0000; + OCR3A = 0x0000; + OCR3B = 0x0000; + ICR3 = 0x0000; + TIMSK3 = 0x00; + //TIMSK3 |= 0x21; //ICP and OVF interrupts + TIFR3 = 0x00; //Clear all flags + + tim3_prescaler = (uint8_t)hwCfg->freq_timer_prescaler; + TCCR3B |= tim3_prescaler; //Enable timer + + + //Speed 0 input timer configuration + PRR1 &= ~0x08; //Enable Timer4 power + TCCR4A = 0x00; //OCx disconnected, WGM:0x0 + TCCR4B = 0x80; //ICP Noise filter, Falling edge, no clock + TCCR4C = 0x00; + TCNT4 = 0x0000; + OCR4A = 0x0000; + OCR4B = 0x0000; + ICR4 = 0x0000; + TIMSK4 = 0x00; + //TIMSK4 |= 0x21; //ICP and OVF interrupts + TIFR4 = 0x00; //Clear all flags + + tim4_prescaler = (uint8_t)hwCfg->freq_timer_prescaler; + TCCR4B |= tim4_prescaler; //Enable timer + + + //UART1 configuration + PRR0 &= 0x10; //Enable UART1 power + UCSR1A = 0x00; //Clear flags, Single UART speed, Single processor mode + UCSR1B = 0x18; //Enable RX/TX hardware, 8bit char + //UCSR1B |= 0xC0; //Enable RX/TX interrupt, + UCSR1C = 0x06; ; //async, No parity, 1 stop bit, 8bit char, + UBRR1 = hwCfg->uart_prescaler; //UART baud rate select + + + //"Systick" timer configuration + PRR0 &= ~0x20; //Enable Timer0 power + TCCR0A = 0x02 ;//OC0x not connected, WGM 0x01-CTC OC0A TOP + TCCR0B = 0x00; //WGM 0x01-CTC, No clock + TIMSK0 = 0x00; + //TIMSK0 |= 0x01; //OVF interrupt enabled + TCNT0 = 0x00; + OCR0A = hwCfg->systick_timer_top; + OCR0B= 0x00; + TIFR0 = 0x00; //Reset all flags + + tim0_prescaler = (uint8_t)hwCfg->systick_timer_prescaler; + TCCR0B |= tim0_prescaler; + + + //Disabled not used power configuration + if(hwCfg->disable_unused) + { + //Disable power to not used peripherals + PRR0 |= 0xC6; //Disable TWI0, TIM2, SPI0, UART0 + PRR1 |= 0x34; //Disable TWI1, PRTC, SPI1 + } + + //Watchdog configuration + if(hwCfg->en_watchdog) + { + //watchdog timer setup + WDTCSR |= 0x10; //Change enable + WDTCSR |= 0x0D; //System reset mode, 0.5s period. + //use special instruction to reset watchdog timer + }; +} + + +// Handbrake input +level_t HAL_ReadLvl_Handbrake(void) +{ + if(PINB & 0x40) return HIGH; + else return LOW; +} + +// Brakes input +level_t HAL_ReadLvl_Brake(void) +{ + if(PINB & 0x80) return HIGH; + else return LOW; +} + +// Dimm input +level_t HAL_ReadLvl_Dimm(void) +{ + if(PIND & 0x80) return HIGH; + else return LOW; +} + +// UP button +level_t HAL_ReadLvl_BtnUp(void) +{ + if(PINE & 0x08) return HIGH; + else return LOW; +} + +// Down button +level_t HAL_ReadLvl_BtnDown(void) +{ + if(PINE & 0x02) return HIGH; + else return LOW; +} + +// Mode button +level_t HAL_ReadLvl_BtnMode(void) +{ + if(PINC & 0x20) return HIGH; + else return LOW; +} + +// Handbrake pull +level_t HAL_ReadLvl_HandbrakePull(void) +{ + if(PINB & 0x20) return HIGH; + else return LOW; +} + +// Coil driver control low +level_t HAL_ReadLvl_CoilLow(void) +{ + if(PINB & 0x01) return HIGH; + else return LOW; +} + +// Coil driver control high +level_t HAL_ReadLvl_CoilHigh(void) +{ + if(PINB & 0x02) return HIGH; + else return LOW; +} + +// LED PWM control +level_t HAL_ReadLvl_LedsPwm(void) +{ + if(PINB & 0x04) return HIGH; + else return LOW; +} + +// Speed 0 input pin +level_t HAL_ReadLvl_Speed0(void) +{ + if(PINE & 0x04) return HIGH; + else return LOW; +} + +// Speed 1 input pin +level_t HAL_ReadLvl_Speed1(void) +{ + if(PINE & 0x01) return HIGH; + else return LOW; +} + +// Speed common pull pin +level_t HAL_ReadLvl_SpeedPull(void) +{ + if(PIND & 0x40) return HIGH; + else return LOW; +} + + +// Set handbrake pull-up +void HAL_SetPull_Handbrake(level_t lvl) +{ + switch(lvl) + { + case HIGH: + PORTB |= 0x20; //Set high + DDRB |= 0x20; //Set as output + break; + + case LOW: + PORTB &= ~0x20; //Set low + DDRB |= 0x20; //Set as output + + default: + DDRB &= ~0x20; //Set as input + PORTB |= 0x20; //Set high + break; + } +} + +// Set speed inputs common pull +void HAL_SetPull_Speed(level_t lvl) +{ + switch(lvl) + { + case HIGH: + PORTD |= 0x40; //Set high + break; + + default: + PORTD &= ~0x40; //Set low + break; + } +} + + +// ADC Wake +void HAL_ADC_Wake(void) +{ + //Enable ADC power + PRR0 &= ~0x01; + //Enable ADC + ADCSRA |= 0x80; +} + +// ADC Sleep +void HAL_ADC_Sleep(void) +{ + //wait to finish + while(ADCSRA&0x40); + //Disable ADC + ADCSRA &= ~0x80; + //Disable ADC power + PRR0 |= 0x01; +} + +// ADC Read +uint16_t HAL_ADC_Read(adcCh_t ch) +{ + //check if ADC is enabled + if(!(ADCSRA&0x80)) return 0xFFFF; + + uint8_t mux = (uint8_t)ch; + //Safe guard mux + if(mux > 15) return 0xFFFF; + // Not available channels + if((mux > 8) && (mux<14)) return 0xFFFF; + + ADMUX &= ~0x0F; + ADMUX |= mux; + ADCSRA |= 0x40; + while(ADCSRA&0x40); //wait to finish + return ADC; +} + + +// Coil Driver Low Side control +void HAL_Coil_SetLowSide(uint8_t on) +{ + if(on) PORTB |= 0x01; + else PORTB &= ~0x01; +} + +void HAL_Coil_SetPWM(uint8_t percent) +{ + HAL_PWM_SetDuty100(PWM_COIL, percent); +} + +void HAL_Coil_SetPWM16b(uint16_t value) +{ + HAL_PWM_SetDuty16b(PWM_COIL, value); +} + + +// LED Display +void HAL_LEDS_Set(uint8_t image) +{ + //Read current PORTD pin6, pin7 + uint8_t keep = PORTD & 0xC0; + + //Safe guard display + image &= 0x3F; + + //Calculate new PORTD + keep |= image; + + //Set PORTD + PORTD = keep; +} + +uint8_t HAL_LEDS_Get(void) +{ + return (PIND & 0x3F); +} + +void HAL_LEDS_SetPWM(uint8_t percent) +{ + HAL_PWM_SetDuty100(PWM_LED, percent); +} + + +// PWM Direct functions +void HAL_PWM_Wake(void) +{ + //Enable Timer1 power + PRR0 &= ~0x80; + //Prepare Timer1 settings + TCNT1 = 0x0000; + OCR1A = 0x0000; + OCR1B = 0x0000; + //Enable clock + TCCR1B |= tim1_prescaler; //Enable timer +} + +void HAL_PWM_Sleep(void) +{ + // Turn off outputs + OCR1A = 0x0000; + OCR1B = 0x0000; + // Force timer to bottom + TCNT1 = (ICR1-1); + // Wait for outputs to be off + while((PINB&0x06)!=0x00) continue; + // Disable clock + TCCR1B &= ~0x07; + // Disable Timer1 power + PRR0 |= 0x80; +} + +void HAL_PWM_SetDuty16b(pwmCh_t ch, uint16_t value) +{ + value = 0xFFFF - value; + + uint32_t top = (uint32_t)ICR1; + uint32_t temp = (uint32_t)value * top; + temp = temp/0x0000FFFF; + //Limit temp + if(temp>0x0000FFFF) temp = 0x0000FFFF; + uint16_t ocrx = (uint16_t) temp; + + PWM_SetOCx(ch, ocrx); +} + +void HAL_PWM_SetDuty100(pwmCh_t ch, uint8_t percent) +{ + if(percent > 100) percent = 100; + percent = 100 - percent; + + uint32_t top = (uint32_t)ICR1; + uint32_t temp = (uint32_t)percent * top; + temp = temp/100; + //Limit temp + if(temp>0x0000FFFF) temp = 0x0000FFFF; + uint16_t ocrx = (uint16_t) temp; + + PWM_SetOCx(ch, ocrx); +} + +/**** Private function definitions ****/ +static void PWM_SetOCx(pwmCh_t ch, uint16_t value) +{ + switch(ch) + { + case PWM_COIL: + OCR1A = value; + return; + + case PWM_LED: + OCR1B = value; + return; + + default: + return; + } +} \ No newline at end of file diff --git a/firmware/src/hw/startup.c b/firmware/src/hw/startup.c deleted file mode 100644 index 7d37254..0000000 --- a/firmware/src/hw/startup.c +++ /dev/null @@ -1,16 +0,0 @@ -/**** Includes ****/ -#include "board/utils/utils.h" -#include "board/setup.h" -#include "startup.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -void hw_startup(void) -{ - bsp_startup(); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/startup.h b/firmware/src/hw/startup.h deleted file mode 100644 index 8d8b511..0000000 --- a/firmware/src/hw/startup.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef HW_STARTUP_H_ -#define HW_STARTUP_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -/**** Public function declarations ****/ -void hw_startup(void); - -#ifdef TESTING -#endif - -#endif /* HW_STARTUP_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/coil.c b/firmware/src/logic/coil.c deleted file mode 100644 index 7b32da2..0000000 --- a/firmware/src/logic/coil.c +++ /dev/null @@ -1,41 +0,0 @@ -/**** Includes ****/ -#include "coil.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const int16_t TARGET_HANDBRAKE = COIL_TARGET_HANDBRAKE; -static const int16_t LOCK_VOLTAGE = COIL_LOCK_VOLTAGE; - -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -int16_t coil_target(uint8_t force, uint8_t hbrake_act) -{ - if(hbrake_act) - { - return TARGET_HANDBRAKE; - } - else if(force==0) - { - return 0; - } - else if(force >= 100) - { - return LOCK_VOLTAGE; - } - else - { - // Calculate target - uint32_t t = (uint32_t)force * LOCK_VOLTAGE; - t /= 100; - if(t > LOCK_VOLTAGE) return LOCK_VOLTAGE; - else if(t < 0) return 0; - else return (int16_t)t; - } - - return 0; -} - -/**** Private function definitions ****/ - diff --git a/firmware/src/logic/coil.h b/firmware/src/logic/coil.h deleted file mode 100644 index 6a14495..0000000 --- a/firmware/src/logic/coil.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef COIL_H_ -#define COIL_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define COIL_TARGET_HANDBRAKE -1 -#define COIL_LOCK_VOLTAGE 6500 - -/**** Public function declarations ****/ -int16_t coil_target(uint8_t force, uint8_t hbrake_act); - -#ifdef TESTING -#endif - -#endif /* COIL_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/display.c b/firmware/src/logic/display.c deleted file mode 100644 index d46e8e8..0000000 --- a/firmware/src/logic/display.c +++ /dev/null @@ -1,108 +0,0 @@ -/**** Includes ****/ -#include "display.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint8_t BACKLIGHT_DIMM = DSP_BACKLIGHT_DIMM_PERCENT; -static const uint8_t BACKLIGHT_BRIGTH = DSP_BACKLIGHT_BRIGTH_PERCENT; - -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t img_gen_dot10(uint8_t percent); -static uint8_t img_gen_dot20(uint8_t percent); -static uint8_t img_gen_bar(uint8_t percent); - -/**** Public function definitions ****/ -void dsp_init_ctrl(dsp_ctrl_t* ctrl) -{ - ctrl->img_lock = 0; - ctrl->act_img = 0x00; -} - -void dsp_set_lock(dsp_ctrl_t* ctrl) -{ - ctrl->img_lock = 1; -} - -void dsp_reset_lock(dsp_ctrl_t* ctrl) -{ - ctrl->img_lock = 0; -} - -uint8_t dsp_img_percent(uint8_t value, dsp_style_t style, dsp_ctrl_t* ctrl) -{ - if(ctrl->img_lock) return ctrl->act_img; - - switch(style) - { - case LED_DSP_BAR: - ctrl->act_img = img_gen_bar(value); - break; - - case LED_DSP_DOT10: - ctrl->act_img = img_gen_dot10(value); - break; - - default: - ctrl->act_img = img_gen_dot20(value); - break; - } - - return ctrl->act_img; -} - -uint8_t dsp_img_raw(uint8_t image, dsp_ctrl_t* ctrl) -{ - if(ctrl->img_lock) return ctrl->act_img; - - ctrl->act_img = image & 0x3F; - - return ctrl->act_img; -} - -uint8_t dsp_get_act_img(dsp_ctrl_t* ctrl) -{ - return ctrl->act_img; -} - -uint8_t dsp_backlight(uint8_t dimm_act) -{ - if(dimm_act) return BACKLIGHT_DIMM; - else return BACKLIGHT_BRIGTH; -} - -/**** Private function definitions ****/ -static uint8_t img_gen_dot10(uint8_t percent) -{ - if(percent<6) return 0x01; - else if(percent<16) return 0x03; - else if(percent<26) return 0x02; - else if(percent<36) return 0x06; - else if(percent<46) return 0x04; - else if(percent<56) return 0x0C; - else if(percent<66) return 0x08; - else if(percent<76) return 0x18; - else if(percent<86) return 0x10; - else if(percent<96) return 0x30; - else return 0x20; -} - -static uint8_t img_gen_dot20(uint8_t percent) -{ - if(percent<11) return 0x01; - else if(percent<31) return 0x02; - else if(percent<51) return 0x04; - else if(percent<71) return 0x08; - else if(percent<91) return 0x10; - else return 0x20; -} - -static uint8_t img_gen_bar(uint8_t percent) -{ - if(percent<11) return 0x01; - else if(percent<31) return 0x03; - else if(percent<51) return 0x07; - else if(percent<71) return 0x0F; - else if(percent<91) return 0x1F; - else return 0x3F; -} diff --git a/firmware/src/logic/display.h b/firmware/src/logic/display.h deleted file mode 100644 index 588bd79..0000000 --- a/firmware/src/logic/display.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LOGIC_DISPLAY_H_ -#define LOGIC_DISPLAY_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define DSP_BACKLIGHT_DIMM_PERCENT 50 -#define DSP_BACKLIGHT_BRIGTH_PERCENT 100 - -typedef enum { - LED_DSP_DOT20, - LED_DSP_DOT10, - LED_DSP_BAR -} dsp_style_t; - -typedef struct { - uint8_t img_lock; - uint8_t act_img; -} dsp_ctrl_t; - -/**** Public function declarations ****/ -void dsp_init_ctrl(dsp_ctrl_t* ctrl); - -void dsp_set_lock(dsp_ctrl_t* ctrl); -void dsp_reset_lock(dsp_ctrl_t* ctrl); - -uint8_t dsp_img_percent(uint8_t value, dsp_style_t style, dsp_ctrl_t* ctrl); -uint8_t dsp_img_raw(uint8_t image, dsp_ctrl_t* ctrl); -uint8_t dsp_get_act_img(dsp_ctrl_t* ctrl); - -uint8_t dsp_backlight(uint8_t dimm_act); - -#ifdef TESTING -#endif - -#endif /* LOGIC_DISPLAY_H_ */ diff --git a/firmware/src/logic/force.c b/firmware/src/logic/force.c deleted file mode 100644 index adee492..0000000 --- a/firmware/src/logic/force.c +++ /dev/null @@ -1,63 +0,0 @@ -/**** Includes ****/ -#include "force.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint16_t MAX_HBRAKE_HOLD_TIME = FORCE_MAX_HBRAKE_HOLD_TIME; - -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -#ifdef TESTING -#endif - -uint8_t force_next(uint8_t handbrake, uint8_t brakes, fbrake_mode_t bmode, uint8_t user_force, uint16_t hbarke_act_time) -{ - // Do force logic - if((handbrake)&&(hbarke_act_time < MAX_HBRAKE_HOLD_TIME)) - { - return 0; - } - else if(brakes) - { - switch(bmode) - { - case FORCE_BMODE_KEEP: - return user_force; - break; - - case FORCE_BMODE_LOCK: - return 100; - break; - - default: //FORCE_BMODE_OPEN - return 0; - break; - } - } - else - { - return user_force; - } - - return 0; -} - -fbrake_mode_t force_cycle_bmode(fbrake_mode_t bmode) -{ - switch(bmode) - { - case FORCE_BMODE_OPEN: - return FORCE_BMODE_KEEP; - - case FORCE_BMODE_KEEP: - return FORCE_BMODE_LOCK; - - default: - return FORCE_BMODE_OPEN; - } -} - -/**** Private function definitions ****/ - diff --git a/firmware/src/logic/force.h b/firmware/src/logic/force.h deleted file mode 100644 index 4655b45..0000000 --- a/firmware/src/logic/force.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef FORCE_H_ -#define FORCE_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define FORCE_MAX_HBRAKE_HOLD_TIME 1000 - -typedef enum { - FORCE_BMODE_OPEN, - FORCE_BMODE_KEEP, - FORCE_BMODE_LOCK -} fbrake_mode_t; - -/**** Public function declarations ****/ -uint8_t force_next(uint8_t handbrake, uint8_t brakes, fbrake_mode_t bmode, uint8_t user_force, uint16_t hbarke_act_time); -fbrake_mode_t force_cycle_bmode(fbrake_mode_t bmode); - -#ifdef TESTING -#endif - -#endif /* FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/pot.c b/firmware/src/logic/pot.c deleted file mode 100644 index 932639c..0000000 --- a/firmware/src/logic/pot.c +++ /dev/null @@ -1,42 +0,0 @@ -/**** Includes ****/ -#include "pot.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -#ifdef TESTING -#endif - -uint8_t pot_mv_to_percent(uint16_t value, pot_cfg_t* cfg) -{ - // Setup limits - uint16_t bottom = 0; - uint16_t top = cfg->reference; - - // Adjust for top and bottom deadband - if(bottom < cfg->deadband) bottom = cfg->deadband; - if(top > cfg->deadband) top -= cfg->deadband; - - // Calculate percent - if(value <= bottom) return 0; - else if(value >= top) return 100; - else - { - // Adjust values for offset - if(bottom) - { - value = value - bottom; - top = top - bottom; - }; - - uint32_t y = (uint32_t)value * 100; - y = y/(uint32_t)top; - - return (uint16_t)y; - } -} - -/**** Private function definitions ****/ - diff --git a/firmware/src/logic/pot.h b/firmware/src/logic/pot.h deleted file mode 100644 index d77c46b..0000000 --- a/firmware/src/logic/pot.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef POTENTIOMETER_H_ -#define POTENTIOMETER_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -typedef struct { - uint16_t reference; - uint16_t deadband; -} pot_cfg_t; - -/**** Public function declarations ****/ -uint8_t pot_mv_to_percent(uint16_t value, pot_cfg_t* cfg); - -#ifdef TESTING -#endif - -#endif /* POTENTIOMETER_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/user_force.c b/firmware/src/logic/user_force.c deleted file mode 100644 index 5f856cd..0000000 --- a/firmware/src/logic/user_force.c +++ /dev/null @@ -1,52 +0,0 @@ -/**** Includes ****/ -#include "user_force.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint8_t MAX_PERCENT = USER_FORCE_MAX_PERCENT; -static const uint8_t MIN_PERCENT = USER_FORCE_MIN_PERCENT; - -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -#ifdef TESTING -#endif - -uint8_t user_force_btn(uint8_t prev_force, uint8_t up_act, uint8_t down_act, uint8_t delta) -{ - uint8_t new_froce = prev_force; - - if(up_act) - { - new_froce = prev_force + delta; - // Limit overflow and top value - if(new_froce < prev_force) new_froce = 100; - else if(new_froce < MIN_PERCENT) new_froce = MIN_PERCENT; - }; - - if(down_act) - { - new_froce = prev_force - delta; - // Limit overflow and top value - if(new_froce > prev_force) new_froce = 0; - else if(new_froce > MAX_PERCENT) new_froce = MAX_PERCENT; - }; - - // Do deadband - if(new_froce < MIN_PERCENT) new_froce = 0; - else if(new_froce > MAX_PERCENT) new_froce = 100; - - return new_froce; -} - -uint8_t user_force_pot(uint8_t prev_force, uint8_t pot, uint8_t hyst) -{ - uint8_t new_froce = pot; - - if(new_froce < MIN_PERCENT) new_froce = 0; - else if(new_froce > MAX_PERCENT) new_froce = 100; - - return new_froce; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/logic/user_force.h b/firmware/src/logic/user_force.h deleted file mode 100644 index a7b5aa4..0000000 --- a/firmware/src/logic/user_force.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef USER_FORCE_H_ -#define USER_FORCE_H_ - -/**** Includes ****/ -#include - -/**** Public definitions ****/ -#define USER_FORCE_MAX_PERCENT 90 -#define USER_FORCE_MIN_PERCENT 10 - -/**** Public function declarations ****/ -uint8_t user_force_btn(uint8_t prev_force, uint8_t up_act, uint8_t down_act, uint8_t delta); -uint8_t user_force_pot(uint8_t prev_force, uint8_t pot, uint8_t hyst); - -#ifdef TESTING -#endif - -#endif /* USER_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c deleted file mode 100644 index b1d9d5b..0000000 --- a/firmware/src/main.c +++ /dev/null @@ -1,127 +0,0 @@ -/**** Includes ****/ -#include - -// Hardware IO -#include "hw/startup.h" -#include "hw/analog.h" -#include "hw/buttons.h" -#include "hw/hb_control.h" -#include "hw/led_display.h" - -// Logic blocks -#include "logic/coil.h" -#include "logic/display.h" -#include "logic/force.h" -#include "logic/pot.h" -#include "logic/user_force.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -static volatile uint8_t user_force = 0; -static volatile uint8_t user_force_step = 10; - -static volatile fbrake_mode_t bmode = FORCE_BMODE_OPEN; - -static volatile uint8_t next_force = 0; -static volatile int16_t next_target = 0; - -static volatile hb_feedback_t hb_feedback; -static volatile hb_control_t hb_ctrl; - -static volatile uint8_t backlight = 0; -static volatile dsp_ctrl_t dsp_logic_ctrl; - -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -int main(void) -{ - hw_startup(); - - // Create objects for all buttons - btn_t btn_mode; - btn_t btn_down; - btn_t btn_up; - btn_t btn_dimm; - btn_t btn_brakes; - btn_t btn_handbrake; - btn_t btn_handbrake_dir; - - // Set button pulls - //btn_ch_set_pull(BTN_1, BTN_PULL_HIGH); - //btn_ch_set_pull(BTN_2, BTN_PULL_HIGH); - //btn_ch_set_pull(BTN_3, BTN_PULL_HIGH); - btn_ch_set_pull(BTN_6, BTN_PULL_HIGH); - - // Create object for half-bridge control - - // Initialize half-bridge - hb_init(&hb_feedback, &hb_ctrl); - hb_enable(&hb_ctrl); - - // Init display backlight - dsp_init_ctrl(&dsp_logic_ctrl); - backlight = dsp_backlight(1); - led_dsp_backligth_set(backlight); - - while(1) - { - // Read buttons - btn_ch_process(BTN_1, &btn_mode); - btn_ch_process(BTN_2, &btn_down); - btn_ch_process(BTN_3, &btn_up); - btn_ch_process(BTN_4, &btn_dimm); - btn_ch_process(BTN_5, &btn_brakes); - btn_ch_process(BTN_6, &btn_handbrake); - btn_ch_process(BTN_6N, &btn_handbrake_dir); - - // Process user force changes - if((btn_up.new_state)||(btn_down.new_state)) - { - uint8_t up_act = btn_up.new_state & btn_up.state; - uint8_t down_act = btn_down.new_state & btn_down.state; - - user_force = user_force_btn(user_force, up_act, down_act, user_force_step); - btn_up.new_state = 0; - btn_down.new_state = 0; - }; - - // Process brake mode changes - if(btn_mode.new_state) - { - if(btn_mode.state) bmode = force_cycle_bmode(bmode); - btn_mode.new_state = 0; - }; - - // Calculate next force - next_force = force_next(btn_handbrake.state, btn_brakes.state, bmode, user_force, btn_handbrake.state_time); - - // Calculate next coil target - next_target = coil_target(next_force, btn_handbrake.state); - - // Read Half-bridge status and apply next target - hb_process(next_target, &hb_feedback, &hb_ctrl); - - // Generate image of current force - uint8_t img = 0x00; - - if(hb_feedback.fused) img = 0xAA; - else img = dsp_img_percent(next_force, LED_DSP_DOT10); - - // Apply image - led_dsp_set_image(img); - - // Process display backlight - if(btn_dimm.new_state) - { - backlight = dsp_backlight(btn_dimm.state); - led_dsp_backligth_set(backlight); - btn_dimm.new_state = 0; - }; - } - - return 0; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp new file mode 100644 index 0000000..77f92f9 --- /dev/null +++ b/firmware/src/main.cpp @@ -0,0 +1,18 @@ +/* + * uDCCD.cpp + * + * Created: 12.03.2024 20:39:27 + * Author : User + */ + +#include + + +int main(void) +{ + /* Replace with your application code */ + while (1) + { + } +} + diff --git a/firmware/src/uDCCD_Controller.atsln b/firmware/src/uDCCD.atsln similarity index 85% rename from firmware/src/uDCCD_Controller.atsln rename to firmware/src/uDCCD.atsln index c9942df..b3332d3 100644 --- a/firmware/src/uDCCD_Controller.atsln +++ b/firmware/src/uDCCD.atsln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Atmel Studio Solution File, Format Version 11.00 VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "uDCCD_Controller", "uDCCD_Controller.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}" +Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "uDCCD", "uDCCD.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/firmware/src/uDCCD_Controller.componentinfo.xml b/firmware/src/uDCCD.componentinfo.xml similarity index 96% rename from firmware/src/uDCCD_Controller.componentinfo.xml rename to firmware/src/uDCCD.componentinfo.xml index 49393d5..03bd101 100644 --- a/firmware/src/uDCCD_Controller.componentinfo.xml +++ b/firmware/src/uDCCD.componentinfo.xml @@ -41,7 +41,7 @@ template source C Exe - YLr2MkKo6ZooP7MhARWYNA== + KjvOcFWd++tbnsEMfVPd/w== templates/main.c Main file (.c) @@ -52,7 +52,7 @@ template source C Exe - mkKaE95TOoATsuBGv6jmxg== + w5aB/d0+DbxGZ7yY0aMMjw== templates/main.cpp Main file (.cpp) diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj new file mode 100644 index 0000000..de25ed8 --- /dev/null +++ b/firmware/src/uDCCD.cppproj @@ -0,0 +1,161 @@ + + + + 2.0 + 7.0 + com.Atmel.AVRGCC8.CPP + dce6c7e3-ee26-4d79-826b-08594b9ad897 + ATmega328PB + none + Executable + CPP + $(MSBuildProjectName) + .elf + $(MSBuildProjectDirectory)\$(Configuration) + uDCCD + uDCCD + uDCCD + Native + true + false + true + true + + + true + + 2 + 0 + 0 + + + + + + + + + + + + + + + + + + -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" + True + True + True + True + False + True + True + + + NDEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize for size (-Os) + True + True + True + True + True + + + NDEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize for size (-Os) + True + True + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + + + + + + + -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" + True + True + True + True + False + True + True + + + DEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize debugging experience (-Og) + True + True + Default (-g2) + True + True + True + + + DEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize debugging experience (-Og) + True + True + Default (-g2) + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Default (-Wa,-g) + + + + + + compile + + + + \ No newline at end of file diff --git a/firmware/src/uDCCD_Controller.cproj b/firmware/src/uDCCD_Controller.cproj deleted file mode 100644 index fd9019a..0000000 --- a/firmware/src/uDCCD_Controller.cproj +++ /dev/null @@ -1,276 +0,0 @@ - - - - 2.0 - 7.0 - com.Atmel.AVRGCC8.C - dce6c7e3-ee26-4d79-826b-08594b9ad897 - ATmega328PB - none - Executable - C - $(MSBuildProjectName) - .elf - $(MSBuildProjectDirectory)\$(Configuration) - uDCCD_Controller - uDCCD_Controller - uDCCD_Controller - Native - true - false - true - true - 0x20000000 - - true - exception_table - 2 - 0 - 1 - - - - - - - - - - - - - - com.atmel.avrdbg.tool.atmelice - J42700001490 - 0x1E9516 - - - - 125000 - - debugWIRE - - com.atmel.avrdbg.tool.atmelice - J42700001490 - Atmel-ICE - - debugWIRE - 125000 - - - - - -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" - True - True - True - True - False - True - True - - - NDEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Optimize for size (-Os) - True - True - True - - - libm - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - - - - - - - -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" - True - True - True - True - False - True - True - - - DEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - True - True - Default (-g2) - True - - - libm - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Default (-Wa,-g) - - - - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - - - - - - - - - \ No newline at end of file diff --git a/firmware/tests/Units_Tests.workspace b/firmware/tests/Units_Tests.workspace deleted file mode 100644 index 35d25d9..0000000 --- a/firmware/tests/Units_Tests.workspace +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/firmware/tests/Units_Tests.workspace.layout b/firmware/tests/Units_Tests.workspace.layout deleted file mode 100644 index 3fa6504..0000000 --- a/firmware/tests/Units_Tests.workspace.layout +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/firmware/tests/bsp_main.c b/firmware/tests/bsp_main.c deleted file mode 100644 index fe5a2b9..0000000 --- a/firmware/tests/bsp_main.c +++ /dev/null @@ -1,147 +0,0 @@ -#include -#include - -#include "ut_utils/ut_utils.h" -#include "ut_utils/ut_fault.h" -#include "ut_utils/ut_fuses.h" - -#include "ut_board/ut_ain.h" -#include "ut_board/ut_din.h" -#include "ut_board/ut_dout.h" -#include "ut_board/ut_halfbridge.h" -#include "ut_board/ut_odout.h" -#include "ut_board/ut_setup.h" - -int main() -{ - int tr; - int pass = 1; - - /* UTILITIES TESTS */ - printf("******************************************************\n"); - - tr = ut_util_invert_8b_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_add_8b_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_subtract_8b_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_util_sat_add_16b_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_subtract_16b_test(); - if(!tr) pass = 0; - - tr = ut_util_limit_u32b_to_u16b_test(); - if(!tr) pass = 0; - - tr = ut_util_limit_s32b_to_u16b_test(); - if(!tr) pass = 0; - - tr = ut_util_convert_muldivoff_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_mul_kilo_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_div_kilo_test(); - if(!tr) pass = 0; - - tr = ut_util_sat_ratio_16b_test(); - if(!tr) pass = 0; - - tr = ut_util_percent_to_16b_test(); - if(!tr) pass = 0; - - /* FAULT TESTS */ - printf("******************************************************\n"); - - tr = ut_fault_process_test(); - if(!tr) pass = 0; - - tr = ut_fault_is_active_test(); - if(!tr) pass = 0; - - tr = ut_fault_is_warning_test(); - if(!tr) pass = 0; - - tr = ut_fault_reset_test(); - if(!tr) pass = 0; - - /* FUSE TESTS */ - printf("******************************************************\n"); - - tr = ut_fuse_reset_test(); - if(!tr) pass = 0; - - /* AIN TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_ain_read_test(); - if(!tr) pass = 0; - - /* DIN TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_din_read_test(); - if(!tr) pass = 0; - - /* DOUT TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_dout_write_test(); - if(!tr) pass = 0; - - /* HALFBRIDGE TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_hb_write_low_test(); - if(!tr) pass = 0; - - tr = ut_bsp_hb_write_pwm_test(); - if(!tr) pass = 0; - - tr = ut_bsp_hb_read_meas_test(); - if(!tr) pass = 0; - - /* ODOUT TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_odout_write_test(); - if(!tr) pass = 0; - - tr = ut_bsp_odout_write_common_test(); - if(!tr) pass = 0; - - /* SETUP TESTS */ - printf("******************************************************\n"); - - tr = ut_bsp_startup_test(); - if(!tr) pass = 0; - - /* FINAL RESULT */ - printf("******************************************************\n"); - - tr = ut_util_interpolate_1d_u16b_test(); - if(!tr) pass = 0; - - tr = ut_util_interpolate_2d_u16b_test(); - if(!tr) pass = 0; - - - if(pass) - { - printf("ALL PASS\n\n"); - return 1; - } - else - { - printf("SOME FAIL\n\n"); - return 0; - } - - return 0; -} diff --git a/firmware/tests/hw_main.c b/firmware/tests/hw_main.c deleted file mode 100644 index 442e284..0000000 --- a/firmware/tests/hw_main.c +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include - -#include "ut_hw/ut_analog.h" -#include "ut_hw/ut_buttons.h" -#include "ut_hw/ut_hb_control.h" -#include "ut_hw/ut_led_display.h" -#include "ut_hw/ut_startup.h" - -int main() -{ - int tr; - int pass = 1; - int t_cnt = 0; - int f_cnt = 0; - - /* ANALOG TESTS */ - printf("******************************************************\n"); - - tr = ut_analog_ch_get_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* BUTTON TESTS */ - printf("******************************************************\n"); - - tr = ut_btn_reset_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_btn_process_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_btn_ch_process_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_btn_ch_set_pull_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* HALFBRIDGE CONTROL TESTS */ - printf("******************************************************\n"); - - tr = ut_hb_is_equal_fb_struct_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_hb_is_equal_ctrl_struct_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_hb_init_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_hb_enable_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_hb_disable_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_hb_process_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* DISPLAY TESTS */ - printf("******************************************************\n"); - - tr = ut_led_dsp_set_image_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_led_dsp_backligth_set_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* STARTUP TESTS */ - printf("******************************************************\n"); - - tr = ut_hw_startup_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - printf("******************************************************\n"); - printf("\n%d TESTS DONE\n", t_cnt); - if(pass) - { - printf("ALL PASS\n\n"); - return 1; - } - else - { - printf("%d FAILED\n\n", f_cnt); - return 0; - } - - return 0; -} diff --git a/firmware/tests/logic_main.c b/firmware/tests/logic_main.c deleted file mode 100644 index 66d6d11..0000000 --- a/firmware/tests/logic_main.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#include "ut_logic/ut_coil.h" -#include "ut_logic/ut_display.h" -#include "ut_logic/ut_force.h" -#include "ut_logic/ut_pot.h" -#include "ut_logic/ut_user_force.h" - -int main() -{ - int tr; - int pass = 1; - int t_cnt = 0; - int f_cnt = 0; - - /* COIL TESTS */ - printf("******************************************************\n"); - - tr = ut_coil_target_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* DISPLAY TESTS */ - printf("******************************************************\n"); - - tr = ut_dsp_init_ctrl_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_set_lock_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_reset_lock_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_img_percent_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_img_raw_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_get_act_img_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_dsp_backlight_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* FORCE TESTS */ - printf("******************************************************\n"); - - tr = ut_force_cycle_bmode_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_force_next_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* POT TESTS */ - printf("******************************************************\n"); - - tr = ut_pot_mv_to_percent_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - /* USER FORCE TESTS */ - printf("******************************************************\n"); - - tr = ut_user_force_btn_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - tr = ut_user_force_pot_test(); - t_cnt++; - if(!tr){ pass = 0; f_cnt++;}; - - printf("******************************************************\n"); - printf("\n%d TESTS DONE\n", t_cnt); - if(pass) - { - printf("ALL PASS\n\n"); - return 1; - } - else - { - printf("%d FAILED\n\n", f_cnt); - return 0; - } - - return 0; -} diff --git a/firmware/tests/mock_board/mock_board_ain.c b/firmware/tests/mock_board/mock_board_ain.c deleted file mode 100644 index e7ca8f2..0000000 --- a/firmware/tests/mock_board/mock_board_ain.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "mock_board_ain.h" - -#define AIN_CH_CNT 5 -static uint8_t ain_ch = 0; -static uint16_t ain_data[AIN_CH_CNT+1]; - -uint16_t bsp_ain_read(uint8_t ch) -{ - if(ch > AIN_CH_CNT) return 0; - ain_ch = ch; - return ain_data[ch]; -} - -uint8_t mock_board_ain_read_ch(void) -{ - return ain_ch; -} - -void mock_board_ain_write_ch(uint8_t ch) -{ - ain_ch = ch; -} - -uint16_t mock_board_ain_read_data(uint8_t ch) -{ - return ain_data[ch]; -} - -void mock_board_ain_write_data(uint8_t ch, uint16_t value) -{ - ain_data[ch] = value; -} diff --git a/firmware/tests/mock_board/mock_board_ain.h b/firmware/tests/mock_board/mock_board_ain.h deleted file mode 100644 index 7807d3b..0000000 --- a/firmware/tests/mock_board/mock_board_ain.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MOCK_BOARD_AIN_ -#define MOCK_BOARD_AIN_ - -#include -#include - -#include "..\..\src\hw\board\ain.h" - -uint8_t mock_board_ain_read_ch(void); -void mock_board_ain_write_ch(uint8_t ch); -uint16_t mock_board_ain_read_data(uint8_t ch); -void mock_board_ain_write_data(uint8_t ch, uint16_t value); - -#endif /* MOCK_BOARD_AIN_ */ diff --git a/firmware/tests/mock_board/mock_board_din.c b/firmware/tests/mock_board/mock_board_din.c deleted file mode 100644 index 018d90f..0000000 --- a/firmware/tests/mock_board/mock_board_din.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "mock_board_din.h" - -#define DIN_CH_CNT 9 -static uint8_t din_ch = 0; -static uint8_t din_data[DIN_CH_CNT]; - -uint8_t bsp_din_read(uint8_t ch) -{ - if(ch >= DIN_CH_CNT) return BSP_DIN_LOW; - din_ch = ch; - return din_data[ch]; -} - -uint8_t mock_board_din_read_ch(void) -{ - return din_ch; -} - -void mock_board_din_write_ch(uint8_t ch) -{ - din_ch = ch; -} - -uint8_t mock_board_din_read_data(uint8_t ch) -{ - return din_data[ch]; -} - -void mock_board_din_write_data(uint8_t ch, uint8_t value) -{ - din_data[ch] = value; -} diff --git a/firmware/tests/mock_board/mock_board_din.h b/firmware/tests/mock_board/mock_board_din.h deleted file mode 100644 index d117d60..0000000 --- a/firmware/tests/mock_board/mock_board_din.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MOCK_BOARD_DIN_ -#define MOCK_BOARD_DIN_ - -#include -#include - -#include "..\..\src\hw\board\din.h" - -uint8_t mock_board_din_read_ch(void); -void mock_board_din_write_ch(uint8_t ch); -uint8_t mock_board_din_read_data(uint8_t ch); -void mock_board_din_write_data(uint8_t ch, uint8_t value); - -#endif /* MOCK_BOARD_DIN_ */ diff --git a/firmware/tests/mock_board/mock_board_dout.c b/firmware/tests/mock_board/mock_board_dout.c deleted file mode 100644 index 3dba81b..0000000 --- a/firmware/tests/mock_board/mock_board_dout.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "mock_board_dout.h" - -#define DOUT_CH_CNT 7 -static uint8_t dout_ch = 0; -static int8_t dout_data[DOUT_CH_CNT]; - -void bsp_dout_write(uint8_t ch, int8_t lvl) -{ - if(ch < DOUT_CH_CNT) dout_data[ch] = lvl; - dout_ch = ch; -} - -uint8_t mock_board_dout_read_ch(void) -{ - return dout_ch; -} - -void mock_board_dout_write_ch(uint8_t ch) -{ - dout_ch = ch; -} - -int8_t mock_board_dout_read_data(uint8_t ch) -{ - return dout_data[ch]; -} - -void mock_board_dout_write_data(uint8_t ch, int8_t lvl) -{ - dout_data[ch] = lvl; -} diff --git a/firmware/tests/mock_board/mock_board_dout.h b/firmware/tests/mock_board/mock_board_dout.h deleted file mode 100644 index 8a9e058..0000000 --- a/firmware/tests/mock_board/mock_board_dout.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MOCK_BOARD_DOUT_ -#define MOCK_BOARD_DOUT_ - -#include -#include - -#include "..\..\src\hw\board\dout.h" - -uint8_t mock_board_dout_read_ch(void); -void mock_board_dout_write_ch(uint8_t ch); -int8_t mock_board_dout_read_data(uint8_t ch); -void mock_board_dout_write_data(uint8_t ch, int8_t lvl); - -#endif /* MOCK_BOARD_DOUT_ */ diff --git a/firmware/tests/mock_board/mock_board_halfbridge.c b/firmware/tests/mock_board/mock_board_halfbridge.c deleted file mode 100644 index 93e323f..0000000 --- a/firmware/tests/mock_board/mock_board_halfbridge.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "mock_board_halfbridge.h" - -#define DOUT_CH_CNT 6 -static uint8_t low_ctrl = 0; -static uint16_t pwm_ctrl = 0; -static hb_meas_t feedback; - -void bsp_hb_write_low(uint8_t state) -{ - low_ctrl = state; -} - -void bsp_hb_write_pwm(uint16_t pwm) -{ - pwm_ctrl = pwm; -} - -void bsp_hb_read_meas(hb_meas_t* measurements) -{ - measurements->out_voltage = feedback.out_voltage; - measurements->out_current = feedback.out_current; - measurements->sup_voltage = feedback.sup_voltage; - measurements->sup_current = feedback.sup_current; - measurements->out_power = feedback.out_power; - measurements->sup_power = feedback.sup_power; - measurements->out_impedance = feedback.out_impedance; - measurements->low_side_ctrl = feedback.low_side_ctrl; - measurements->pwm = feedback.pwm; -} - - -uint8_t mock_board_halfbridge_read_low(void) -{ - return low_ctrl; -} - -void mock_board_halfbridge_write_low(uint8_t state) -{ - low_ctrl = state; -} - -uint16_t mock_board_halfbridge_read_pwm(void) -{ - return pwm_ctrl; -} - -void mock_board_halfbridge_write_pwm(uint16_t pwm) -{ - pwm_ctrl = pwm; -} - -void mock_board_halfbridge_read_feedback(hb_meas_t* measurements) -{ - measurements->out_voltage = feedback.out_voltage; - measurements->out_current = feedback.out_current; - measurements->sup_voltage = feedback.sup_voltage; - measurements->sup_current = feedback.sup_current; - measurements->out_power = feedback.out_power; - measurements->sup_power = feedback.sup_power; - measurements->out_impedance = feedback.out_impedance; - measurements->low_side_ctrl = feedback.low_side_ctrl; - measurements->pwm = feedback.pwm; -} - -void mock_board_halfbridge_write_feedback(hb_meas_t* new_fb) -{ - feedback.out_voltage = new_fb->out_voltage; - feedback.out_current = new_fb->out_current; - feedback.sup_voltage = new_fb->sup_voltage; - feedback.sup_current = new_fb->sup_current; - feedback.out_power = new_fb->out_power; - feedback.sup_power = new_fb->sup_power; - feedback.out_impedance = new_fb->out_impedance; - feedback.low_side_ctrl = new_fb->low_side_ctrl; - feedback.pwm = new_fb->pwm; -} diff --git a/firmware/tests/mock_board/mock_board_halfbridge.h b/firmware/tests/mock_board/mock_board_halfbridge.h deleted file mode 100644 index 6949928..0000000 --- a/firmware/tests/mock_board/mock_board_halfbridge.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MOCK_BOARD_HALFBRIDGE_ -#define MOCK_BOARD_HALFBRIDGE_ - -#include -#include - -#include "..\..\src\hw\board\halfbridge.h" - -uint8_t mock_board_halfbridge_read_low(void); -void mock_board_halfbridge_write_low(uint8_t state); - -uint16_t mock_board_halfbridge_read_pwm(void); -void mock_board_halfbridge_write_pwm(uint16_t pwm); - -void mock_board_halfbridge_read_feedback(hb_meas_t* measurements); -void mock_board_halfbridge_write_feedback(hb_meas_t* new_fb); - -#endif /* MOCK_BOARD_HALFBRIDGE_ */ diff --git a/firmware/tests/mock_board/mock_board_odout.c b/firmware/tests/mock_board/mock_board_odout.c deleted file mode 100644 index 33bc914..0000000 --- a/firmware/tests/mock_board/mock_board_odout.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "mock_board_odout.h" - -#define ODOUT_CH_CNT 7 -static uint8_t odout_ch = 0; -static int8_t odout_data[ODOUT_CH_CNT]; -static uint8_t common_pwm = 0; - -void bsp_odout_write(uint8_t ch, int8_t lvl) -{ - if(ch < ODOUT_CH_CNT) odout_data[ch] = lvl; - odout_ch = ch; -} - -void bsp_odout_write_common(uint8_t percent) -{ - common_pwm = percent; -} - -uint8_t mock_board_odout_read_ch(void) -{ - return odout_ch; -} - -void mock_board_odout_write_ch(uint8_t ch) -{ - odout_ch = ch; -} - -int8_t mock_board_odout_read_data(uint8_t ch) -{ - return odout_data[ch]; -} - -void mock_board_odout_write_data(uint8_t ch, int8_t lvl) -{ - odout_data[ch] = lvl; -} - -uint8_t mock_board_odout_read_pwm(void) -{ - return common_pwm; -} - -void mock_board_odout_write_pwm(uint8_t percent) -{ - common_pwm = percent; -} diff --git a/firmware/tests/mock_board/mock_board_odout.h b/firmware/tests/mock_board/mock_board_odout.h deleted file mode 100644 index fed625e..0000000 --- a/firmware/tests/mock_board/mock_board_odout.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MOCK_BOARD_ODOUT_ -#define MOCK_BOARD_ODOUT_ - -#include -#include - -#include "..\..\src\hw\board\odout.h" - -uint8_t mock_board_odout_read_ch(void); -void mock_board_odout_write_ch(uint8_t ch); -int8_t mock_board_odout_read_data(uint8_t ch); -void mock_board_odout_write_data(uint8_t ch, int8_t lvl); -uint8_t mock_board_odout_read_pwm(void); -void mock_board_odout_write_pwm(uint8_t percent); - -#endif /* MOCK_BOARD_ODOUT_ */ diff --git a/firmware/tests/mock_board/mock_board_setup.c b/firmware/tests/mock_board/mock_board_setup.c deleted file mode 100644 index 96c3f7e..0000000 --- a/firmware/tests/mock_board/mock_board_setup.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "mock_board_setup.h" - -static uint8_t setup_called = 0; - -void bsp_startup(void) -{ - setup_called = 1; -} - -uint8_t mock_board_setup_read_called(void) -{ - return setup_called; -} - -void mock_board_setup_reset_called(void) -{ - setup_called = 0; -} diff --git a/firmware/tests/mock_board/mock_board_setup.h b/firmware/tests/mock_board/mock_board_setup.h deleted file mode 100644 index a339808..0000000 --- a/firmware/tests/mock_board/mock_board_setup.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef MOCK_BOARD_SETUP_ -#define MOCK_BOARD_SETUP_ - -#include -#include - -#include "..\..\src\hw\board\setup.h" - -uint8_t mock_board_setup_read_called(void); -void mock_board_setup_reset_called(void); - -#endif /* MOCK_BOARD_SETUP_ */ diff --git a/firmware/tests/mock_mcu/mock_mcu_hal_r8.c b/firmware/tests/mock_mcu/mock_mcu_hal_r8.c deleted file mode 100644 index 7038ce5..0000000 --- a/firmware/tests/mock_mcu/mock_mcu_hal_r8.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "..\..\src\hw\board\mcu\mcu_hal.h" -#include "mock_mcu_hal_r8.h" - -// Startup-Configuration functions -static startupCfg_t saved_hwCfg; - -void mcu_startup(startupCfg_t* hwCfg) -{ - saved_hwCfg.adc_clk = hwCfg->adc_clk; - saved_hwCfg.pwm_clk = hwCfg->pwm_clk; - saved_hwCfg.pwm_top = hwCfg->pwm_top; - saved_hwCfg.pwm_chb_en = hwCfg->pwm_chb_en; -} - -void mock_mcu_startup_read_cfg(startupCfg_t* cfg_out) -{ - cfg_out->adc_clk = saved_hwCfg.adc_clk; - cfg_out->pwm_clk = saved_hwCfg.pwm_clk; - cfg_out->pwm_top = saved_hwCfg.pwm_top; - cfg_out->pwm_chb_en = saved_hwCfg.pwm_chb_en; -} - -void mock_mcu_startup_write_cfg(startupCfg_t* cfg_in) -{ - saved_hwCfg.adc_clk = cfg_in->adc_clk; - saved_hwCfg.pwm_clk = cfg_in->pwm_clk; - saved_hwCfg.pwm_top = cfg_in->pwm_top; - saved_hwCfg.pwm_chb_en = cfg_in->pwm_chb_en; -} - -// ADC Interface functions -#define ADC_CH_CNT 16 -static uint8_t adc_ch = 0; -static uint16_t adc_raw[ADC_CH_CNT]; - -uint16_t mcu_adc_read(uint8_t ch) -{ - adc_ch = ch; - - //Safe guard mux - if(ch > (ADC_CH_CNT-1)) return 0xFFFF; - // Not available channels - if((ch > 8) && (ch<14)) return 0xFFFF; - - return adc_raw[ch]; -} - -uint8_t mock_mcu_adc_read_ch(void) -{ - return adc_ch; -} - -void mock_mcu_adc_set_ch(uint8_t new_ch) -{ - adc_ch = new_ch; -} - -uint16_t mock_mcu_adc_read_raw(uint8_t ch) -{ - return adc_raw[ch]; -} - -void mock_mcu_adc_set_raw(uint16_t new_adc_out, uint8_t ch) -{ - adc_raw[ch] = new_adc_out; -} - -// PWM Timer Interface functions -#define PWM_CH_CNT 2 -static uint8_t pwm_ch = 0; -static uint16_t pwm_raw[PWM_CH_CNT]; - -void mcu_pwm_write(uint8_t ch, uint16_t dc) -{ - pwm_ch = ch; - if(ch < PWM_CH_CNT) pwm_raw[ch] = dc; -} - -uint16_t mcu_pwm_read(uint8_t ch) -{ - pwm_ch = ch; - if(ch < PWM_CH_CNT) return pwm_raw[ch]; - else return 0; -} - -uint8_t mock_mcu_pwm_read_ch(void) -{ - return pwm_ch; -} - -void mock_mcu_pwm_set_ch(uint8_t new_ch) -{ - pwm_ch = new_ch; -} - -uint16_t mock_mcu_pwm_read_raw(uint8_t ch) -{ - return pwm_raw[ch]; -} - -void mock_mcu_pwm_set_raw(uint16_t new_pwm, uint8_t ch) -{ - pwm_raw[ch] = new_pwm; -} - -// GPIO Functions -#define GPIO_CH_CNT 18 -static uint8_t gpio_ch = 0; -static uint8_t gpio_input_lvl[GPIO_CH_CNT]; -static int8_t gpio_output_lvl[GPIO_CH_CNT]; - -uint8_t mcu_gpio_read(uint8_t ch) -{ - gpio_ch = ch; - if(ch < GPIO_CH_CNT) return gpio_input_lvl[ch]; - else return 0; -} - -void mcu_gpio_write(uint8_t ch, int8_t lvl) -{ - gpio_ch = ch; - if(ch < GPIO_CH_CNT) gpio_output_lvl[ch] = lvl; -} - -uint8_t mock_mcu_gpio_read_ch(void) -{ - return gpio_ch; -} - -void mock_mcu_gpio_set_ch(uint8_t new_ch) -{ - gpio_ch = new_ch; -} - -int8_t mock_mcu_gpio_read_output_lvl(uint8_t ch) -{ - return gpio_output_lvl[ch]; -} - -void mock_mcu_gpio_set_output_lvl(int8_t new_output_lvl, uint8_t ch) -{ - gpio_output_lvl[ch] = new_output_lvl; -} - -void mock_mcu_gpio_set_input_lvl(uint8_t new_in_lvl, uint8_t ch) -{ - gpio_input_lvl[ch] = new_in_lvl; -} - -// EEPROM functions -uint8_t mcu_ee_read8b(uint16_t address) -{ - return 0x00; -} - -uint16_t mcu_ee_read16b(uint16_t address) -{ - return 0x0000; -} - -uint32_t mcu_ee_read32b(uint16_t address) -{ - return 0x00000000; -} - -void mcu_ee_write8b(uint16_t address, uint8_t value) -{ - -} - -void mcu_ee_write16b(uint16_t address, uint16_t value) -{ -} - -void mcu_ee_write32b(uint16_t address, uint32_t value) -{ - -} diff --git a/firmware/tests/mock_mcu/mock_mcu_hal_r8.h b/firmware/tests/mock_mcu/mock_mcu_hal_r8.h deleted file mode 100644 index 1303302..0000000 --- a/firmware/tests/mock_mcu/mock_mcu_hal_r8.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MOCK_MCU_HAL_R8_ -#define MOCK_MCU_HAL_R8_ - -#include -#include - -#include "../../src/hw/board/mcu/mcu_hal.h" - -void mock_mcu_startup_read_cfg(startupCfg_t* cfg_out); -void mock_mcu_startup_write_cfg(startupCfg_t* cfg_in); - -uint8_t mock_mcu_adc_read_ch(void); -void mock_mcu_adc_set_ch(uint8_t ch); -uint16_t mock_mcu_adc_read_raw(uint8_t ch); -void mock_mcu_adc_set_raw(uint16_t new_adc_out, uint8_t ch); - -uint8_t mock_mcu_pwm_read_ch(void); -void mock_mcu_pwm_set_ch(uint8_t new_ch); -uint16_t mock_mcu_pwm_read_raw(uint8_t ch); -void mock_mcu_pwm_set_raw(uint16_t new_pwm, uint8_t ch); - -uint8_t mock_mcu_gpio_read_ch(void); -void mock_mcu_gpio_set_ch(uint8_t new_ch); -int8_t mock_mcu_gpio_read_output_lvl(uint8_t ch); -void mock_mcu_gpio_set_output_lvl(int8_t new_output_lvl, uint8_t ch); -void mock_mcu_gpio_set_input_lvl(uint8_t new_in_lvl, uint8_t ch); - -#endif /* MOCK_MCU_HAL_R8_ */ diff --git a/firmware/tests/uDCCD_Unit_Tests_BSP.cbp b/firmware/tests/uDCCD_Unit_Tests_BSP.cbp deleted file mode 100644 index 563b6aa..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_BSP.cbp +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - diff --git a/firmware/tests/uDCCD_Unit_Tests_BSP.depend b/firmware/tests/uDCCD_Unit_Tests_BSP.depend deleted file mode 100644 index 5bd0319..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_BSP.depend +++ /dev/null @@ -1,348 +0,0 @@ -# depslib dependency file v1.0 -1698696655 source:f:\microrally\udccd_controller\src\hw\board\ain.c - "utils/utils.h" - "mcu/mcu_hal.h" - "ain.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\utils.h - - -1698776270 f:\microrally\udccd_controller\src\hw\board\mcu\mcu_hal.h - - -1699031869 f:\microrally\udccd_controller\src\hw\board\ain.h - - -1698336545 source:f:\microrally\udccd_controller\src\hw\board\din.c - "utils/utils.h" - "mcu/mcu_hal.h" - "din.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\din.h - - -1698740890 source:f:\microrally\udccd_controller\src\hw\board\dout.c - "utils/utils.h" - "mcu/mcu_hal.h" - "dout.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\dout.h - - -1698575529 source:f:\microrally\udccd_controller\src\hw\board\halfbridge.c - "utils/utils.h" - "mcu/mcu_hal.h" - "halfbridge.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\halfbridge.h - - -1697628825 source:f:\microrally\udccd_controller\src\hw\board\odout.c - "utils/utils.h" - "mcu/mcu_hal.h" - "odout.h" - -1699197585 f:\microrally\udccd_controller\src\hw\board\odout.h - - -1698336545 source:f:\microrally\udccd_controller\src\hw\board\setup.c - "utils/utils.h" - "mcu/mcu_hal.h" - "setup.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\setup.h - - -1699031869 source:f:\microrally\udccd_controller\src\hw\board\utils\faults.c - "faults.h" - "utils.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\faults.h - - -1699031869 source:f:\microrally\udccd_controller\src\hw\board\utils\fuses.c - "fuses.h" - "utils.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\fuses.h - - -1699031869 source:f:\microrally\udccd_controller\src\hw\board\utils\utils.c - "utils.h" - -1699205020 source:f:\microrally\udccd_controller\tests\bsp_main.c - - - "ut_utils/ut_utils.h" - "ut_utils/ut_fault.h" - "ut_utils/ut_fuses.h" - "ut_board/ut_ain.h" - "ut_board/ut_din.h" - "ut_board/ut_dout.h" - "ut_board/ut_halfbridge.h" - "ut_board/ut_odout.h" - "ut_board/ut_setup.h" - -1699204674 f:\microrally\udccd_controller\tests\ut_utils\ut_utils.h - - - -1698870535 f:\microrally\udccd_controller\tests\ut_utils\ut_fault.h - - - -1698959419 f:\microrally\udccd_controller\tests\ut_utils\ut_fuses.h - - - -1699031869 f:\microrally\udccd_controller\tests\ut_board\ut_ain.h - - - -1699041803 f:\microrally\udccd_controller\tests\ut_board\ut_din.h - - - -1699042443 f:\microrally\udccd_controller\tests\ut_board\ut_dout.h - - - -1699051096 f:\microrally\udccd_controller\tests\ut_board\ut_halfbridge.h - - - -1699200555 source:f:\microrally\udccd_controller\tests\mock_mcu\mock_mcu_hal_r8.c - "..\..\src\hw\board\mcu\mcu_hal.h" - "mock_mcu_hal_r8.h" - -1699200561 f:\microrally\udccd_controller\tests\mock_mcu\mock_mcu_hal_r8.h - - - "../../src/hw/board/mcu/mcu_hal.h" - -1699196285 source:f:\microrally\udccd_controller\tests\ut_board\ut_ain.c - "ut_ain.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\ain.h" - -1699196304 source:f:\microrally\udccd_controller\tests\ut_board\ut_din.c - "ut_din.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\din.h" - -1699196320 source:f:\microrally\udccd_controller\tests\ut_board\ut_dout.c - "ut_dout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\dout.h" - -1699196359 source:f:\microrally\udccd_controller\tests\ut_board\ut_halfbridge.c - "ut_dout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\halfbridge.h" - -1699196427 source:f:\microrally\udccd_controller\tests\ut_utils\ut_fault.c - "ut_fault.h" - "..\..\src\hw\board\utils\faults.h" - -1699196445 source:f:\microrally\udccd_controller\tests\ut_utils\ut_fuses.c - "ut_fuses.h" - "..\..\src\hw\board\utils\fuses.h" - -1699203820 source:f:\microrally\udccd_controller\tests\ut_utils\ut_utils.c - "ut_utils.h" - "..\..\src\hw\board\utils\utils.h" - -1699200030 f:\microrally\udccd_controller\tests\ut_board\ut_odout.h - - - -1699200020 source:f:\microrally\udccd_controller\tests\ut_board\ut_odout.c - "ut_odout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\odout.h" - -1699201018 f:\microrally\udccd_controller\tests\ut_board\ut_setup.h - - - -1699201095 source:f:\microrally\udccd_controller\tests\ut_board\ut_setup.c - "ut_odout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\setup.h" - -1699018375 source:d:\microrally\udccd_controller\src\hw\board\ain.c - "utils/utils.h" - "mcu/mcu_hal.h" - "ain.h" - -1699007962 d:\microrally\udccd_controller\src\hw\board\utils\utils.h - - -1699017409 d:\microrally\udccd_controller\src\hw\board\mcu\mcu_hal.h - - -1699535273 d:\microrally\udccd_controller\src\hw\board\ain.h - - "config.h" - -1698333216 source:d:\microrally\udccd_controller\src\hw\board\din.c - "utils/utils.h" - "mcu/mcu_hal.h" - "din.h" - -1699535289 d:\microrally\udccd_controller\src\hw\board\din.h - - -1698673847 source:d:\microrally\udccd_controller\src\hw\board\dout.c - "utils/utils.h" - "mcu/mcu_hal.h" - "dout.h" - -1699535296 d:\microrally\udccd_controller\src\hw\board\dout.h - - -1698656931 source:d:\microrally\udccd_controller\src\hw\board\halfbridge.c - "utils/utils.h" - "mcu/mcu_hal.h" - "halfbridge.h" - -1699535318 d:\microrally\udccd_controller\src\hw\board\halfbridge.h - - "config.h" - -1697618884 source:d:\microrally\udccd_controller\src\hw\board\odout.c - "utils/utils.h" - "mcu/mcu_hal.h" - "odout.h" - -1699257917 d:\microrally\udccd_controller\src\hw\board\odout.h - - -1698328348 source:d:\microrally\udccd_controller\src\hw\board\setup.c - "utils/utils.h" - "mcu/mcu_hal.h" - "setup.h" - -1699017683 d:\microrally\udccd_controller\src\hw\board\setup.h - - -1699007034 source:d:\microrally\udccd_controller\src\hw\board\utils\faults.c - "faults.h" - "utils.h" - -1699007033 d:\microrally\udccd_controller\src\hw\board\utils\faults.h - - -1699007093 source:d:\microrally\udccd_controller\src\hw\board\utils\fuses.c - "fuses.h" - "utils.h" - -1699007094 d:\microrally\udccd_controller\src\hw\board\utils\fuses.h - - -1699264217 source:d:\microrally\udccd_controller\src\hw\board\utils\utils.c - "utils.h" - -1699257917 source:d:\microrally\udccd_controller\tests\bsp_main.c - - - "ut_utils/ut_utils.h" - "ut_utils/ut_fault.h" - "ut_utils/ut_fuses.h" - "ut_board/ut_ain.h" - "ut_board/ut_din.h" - "ut_board/ut_dout.h" - "ut_board/ut_halfbridge.h" - "ut_board/ut_odout.h" - "ut_board/ut_setup.h" - -1699257917 d:\microrally\udccd_controller\tests\ut_utils\ut_utils.h - - - -1698921312 d:\microrally\udccd_controller\tests\ut_utils\ut_fault.h - - - -1699004735 d:\microrally\udccd_controller\tests\ut_utils\ut_fuses.h - - - -1699018575 d:\microrally\udccd_controller\tests\ut_board\ut_ain.h - - - -1699257917 d:\microrally\udccd_controller\tests\ut_board\ut_din.h - - - -1699257917 d:\microrally\udccd_controller\tests\ut_board\ut_dout.h - - - -1699257917 d:\microrally\udccd_controller\tests\ut_board\ut_halfbridge.h - - - -1699257917 d:\microrally\udccd_controller\tests\ut_board\ut_odout.h - - - -1699257917 d:\microrally\udccd_controller\tests\ut_board\ut_setup.h - - - -1699257917 source:d:\microrally\udccd_controller\tests\mock_mcu\mock_mcu_hal_r8.c - "..\..\src\hw\board\mcu\mcu_hal.h" - "mock_mcu_hal_r8.h" - -1699257917 d:\microrally\udccd_controller\tests\mock_mcu\mock_mcu_hal_r8.h - - - "../../src/hw/board/mcu/mcu_hal.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_ain.c - "ut_ain.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\ain.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_din.c - "ut_din.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\din.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_dout.c - "ut_dout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\dout.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_halfbridge.c - "ut_dout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\halfbridge.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_odout.c - "ut_odout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\odout.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_board\ut_setup.c - "ut_odout.h" - "..\mock_mcu\mock_mcu_hal_r8.h" - "..\..\src\hw\board\setup.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_utils\ut_fault.c - "ut_fault.h" - "..\..\src\hw\board\utils\faults.h" - -1699257917 source:d:\microrally\udccd_controller\tests\ut_utils\ut_fuses.c - "ut_fuses.h" - "..\..\src\hw\board\utils\fuses.h" - -1699264250 source:d:\microrally\udccd_controller\tests\ut_utils\ut_utils.c - "ut_utils.h" - "..\..\src\hw\board\utils\utils.h" - -1699535389 d:\microrally\udccd_controller\src\hw\board\config.h - - diff --git a/firmware/tests/uDCCD_Unit_Tests_BSP.layout b/firmware/tests/uDCCD_Unit_Tests_BSP.layout deleted file mode 100644 index 566a1dd..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_BSP.layout +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/firmware/tests/uDCCD_Unit_Tests_HW.cbp b/firmware/tests/uDCCD_Unit_Tests_HW.cbp deleted file mode 100644 index 808a48b..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_HW.cbp +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - diff --git a/firmware/tests/uDCCD_Unit_Tests_HW.depend b/firmware/tests/uDCCD_Unit_Tests_HW.depend deleted file mode 100644 index 4507af5..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_HW.depend +++ /dev/null @@ -1,378 +0,0 @@ -# depslib dependency file v1.0 -1699007034 source:d:\microrally\udccd_controller\src\hw\board\utils\faults.c - "faults.h" - "utils.h" - -1699007033 d:\microrally\udccd_controller\src\hw\board\utils\faults.h - - -1699007962 d:\microrally\udccd_controller\src\hw\board\utils\utils.h - - -1699007093 source:d:\microrally\udccd_controller\src\hw\board\utils\fuses.c - "fuses.h" - "utils.h" - -1699007094 d:\microrally\udccd_controller\src\hw\board\utils\fuses.h - - -1699264217 source:d:\microrally\udccd_controller\src\hw\board\utils\utils.c - "utils.h" - -1698741271 source:d:\microrally\udccd_controller\src\hw\analog.c - "board/utils/utils.h" - "board/ain.h" - "analog.h" - -1699535273 d:\microrally\udccd_controller\src\hw\board\ain.h - - "config.h" - -1699017683 d:\microrally\udccd_controller\src\hw\analog.h - - -1699283768 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_dout.c - "mock_board_dout.h" - -1699535296 d:\microrally\udccd_controller\src\hw\board\dout.h - - -1699272290 d:\microrally\udccd_controller\tests\mock_board\mock_board_dout.h - - - "..\..\src\hw\board\dout.h" - -1699283776 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_odout.c - "mock_board_odout.h" - -1699257917 d:\microrally\udccd_controller\src\hw\board\odout.h - - -1699272272 d:\microrally\udccd_controller\tests\mock_board\mock_board_odout.h - - - "..\..\src\hw\board\odout.h" - -1699272265 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_setup.c - "mock_board_setup.h" - -1699017683 d:\microrally\udccd_controller\src\hw\board\setup.h - - -1699272264 d:\microrally\udccd_controller\tests\mock_board\mock_board_setup.h - - - "..\..\src\hw\board\setup.h" - -1699516357 source:d:\microrally\udccd_controller\tests\hw_main.c - - - "ut_hw/ut_analog.h" - "ut_hw/ut_buttons.h" - "ut_hw/ut_hb_control.h" - "ut_hw/ut_led_display.h" - "ut_hw/ut_startup.h" - -1699271694 d:\microrally\udccd_controller\tests\ut_hw\ut_analog.h - - - -1699272372 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_ain.c - "mock_board_ain.h" - -1699272245 d:\microrally\udccd_controller\tests\mock_board\mock_board_ain.h - - - "..\..\src\hw\board\ain.h" - -1699283758 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_din.c - "mock_board_din.h" - -1699535289 d:\microrally\udccd_controller\src\hw\board\din.h - - -1699272297 d:\microrally\udccd_controller\tests\mock_board\mock_board_din.h - - - "..\..\src\hw\board\din.h" - -1699535318 d:\microrally\udccd_controller\src\hw\board\halfbridge.h - - "config.h" - -1699272285 source:d:\microrally\udccd_controller\tests\mock_board\mock_board_halfbridge.c - "mock_board_halfbridge.h" - -1699287859 d:\microrally\udccd_controller\tests\mock_board\mock_board_halfbridge.h - - - "..\..\src\hw\board\halfbridge.h" - -1699276363 source:d:\microrally\udccd_controller\tests\ut_hw\ut_analog.c - "ut_analog.h" - "..\mock_board\mock_board_ain.h" - "..\..\src\hw\analog.h" - -1699284536 source:d:\microrally\udccd_controller\tests\ut_hw\ut_buttons.c - "ut_buttons.h" - "..\mock_board\mock_board_din.h" - "..\mock_board\mock_board_dout.h" - "..\..\src\hw\buttons.h" - -1699284495 d:\microrally\udccd_controller\tests\ut_hw\ut_buttons.h - - - -1699535453 d:\microrally\udccd_controller\src\hw\buttons.h - - "config.h" - -1699516357 source:d:\microrally\udccd_controller\src\hw\buttons.c - "board/utils/utils.h" - "board/din.h" - "board/dout.h" - "buttons.h" - -1699373886 source:d:\microrally\udccd_controller\src\hw\hb_control.c - "board/utils/utils.h" - "board/halfbridge.h" - "hb_control.h" - -1699351840 d:\microrally\udccd_controller\src\hw\hb_control.h - - "board/utils/faults.h" - "board/utils/fuses.h" - "board/halfbridge.h" - -1699286008 source:d:\microrally\udccd_controller\src\hw\led_display.c - "board/odout.h" - "led_display.h" - -1699017683 d:\microrally\udccd_controller\src\hw\led_display.h - - -1698328349 source:d:\microrally\udccd_controller\src\hw\startup.c - "board/utils/utils.h" - "board/setup.h" - "startup.h" - -1699017682 d:\microrally\udccd_controller\src\hw\startup.h - - -1699285771 d:\microrally\udccd_controller\tests\ut_hw\ut_startup.h - - - -1699285777 source:d:\microrally\udccd_controller\tests\ut_hw\ut_startup.c - "ut_startup.h" - "..\mock_board\mock_board_setup.h" - "..\..\src\hw\startup.h" - -1699286754 source:d:\microrally\udccd_controller\tests\ut_hw\ut_led_display.c - "ut_led_display.h" - "..\mock_board\mock_board_odout.h" - "..\..\src\hw\led_display.h" - -1699286570 d:\microrally\udccd_controller\tests\ut_hw\ut_led_display.h - - - -1699375169 source:d:\microrally\udccd_controller\tests\ut_hw\ut_hb_control.c - "ut_hb_control.h" - "..\mock_board\mock_board_halfbridge.h" - "..\..\src\hw\hb_control.h" - -1699371510 d:\microrally\udccd_controller\tests\ut_hw\ut_hb_control.h - - - -1699031869 source:f:\microrally\udccd_controller\src\hw\board\utils\faults.c - "faults.h" - "utils.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\faults.h - - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\utils.h - - -1699031869 source:f:\microrally\udccd_controller\src\hw\board\utils\fuses.c - "fuses.h" - "utils.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\utils\fuses.h - - -1698697335 source:f:\microrally\udccd_controller\src\hw\analog.c - "board/utils/utils.h" - "board/ain.h" - "analog.h" - -1699476816 f:\microrally\udccd_controller\src\hw\board\ain.h - - -1699031869 f:\microrally\udccd_controller\src\hw\analog.h - - -1699298687 source:f:\microrally\udccd_controller\src\hw\board\utils\utils.c - "utils.h" - -1699298687 source:f:\microrally\udccd_controller\src\hw\buttons.c - "board/utils/utils.h" - "board/din.h" - "board/dout.h" - "buttons.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\din.h - - -1699031869 f:\microrally\udccd_controller\src\hw\board\dout.h - - -1699477045 f:\microrally\udccd_controller\src\hw\buttons.h - - -1699298687 source:f:\microrally\udccd_controller\src\hw\led_display.c - "board/odout.h" - "led_display.h" - -1699197585 f:\microrally\udccd_controller\src\hw\board\odout.h - - -1699031869 f:\microrally\udccd_controller\src\hw\led_display.h - - -1698336545 source:f:\microrally\udccd_controller\src\hw\startup.c - "board/utils/utils.h" - "board/setup.h" - "startup.h" - -1699031869 f:\microrally\udccd_controller\src\hw\board\setup.h - - -1699031869 f:\microrally\udccd_controller\src\hw\startup.h - - -1699474639 source:f:\microrally\udccd_controller\tests\hw_main.c - - - "ut_hw/ut_analog.h" - "ut_hw/ut_buttons.h" - "ut_hw/ut_hb_control.h" - "ut_hw/ut_led_display.h" - "ut_hw/ut_startup.h" - -1699298688 f:\microrally\udccd_controller\tests\ut_hw\ut_analog.h - - - -1699298688 f:\microrally\udccd_controller\tests\ut_hw\ut_buttons.h - - - -1699298688 f:\microrally\udccd_controller\tests\ut_hw\ut_startup.h - - - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_ain.c - "mock_board_ain.h" - -1699298687 f:\microrally\udccd_controller\tests\mock_board\mock_board_ain.h - - - "..\..\src\hw\board\ain.h" - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_din.c - "mock_board_din.h" - -1699298687 f:\microrally\udccd_controller\tests\mock_board\mock_board_din.h - - - "..\..\src\hw\board\din.h" - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_dout.c - "mock_board_dout.h" - -1699298687 f:\microrally\udccd_controller\tests\mock_board\mock_board_dout.h - - - "..\..\src\hw\board\dout.h" - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_halfbridge.c - "mock_board_halfbridge.h" - -1699298687 f:\microrally\udccd_controller\tests\mock_board\mock_board_halfbridge.h - - - "..\..\src\hw\board\halfbridge.h" - -1699476970 f:\microrally\udccd_controller\src\hw\board\halfbridge.h - - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_odout.c - "mock_board_odout.h" - -1699298687 f:\microrally\udccd_controller\tests\mock_board\mock_board_odout.h - - - "..\..\src\hw\board\odout.h" - -1699298687 source:f:\microrally\udccd_controller\tests\mock_board\mock_board_setup.c - "mock_board_setup.h" - -1699298688 f:\microrally\udccd_controller\tests\mock_board\mock_board_setup.h - - - "..\..\src\hw\board\setup.h" - -1699298688 source:f:\microrally\udccd_controller\tests\ut_hw\ut_analog.c - "ut_analog.h" - "..\mock_board\mock_board_ain.h" - "..\..\src\hw\analog.h" - -1699298688 source:f:\microrally\udccd_controller\tests\ut_hw\ut_buttons.c - "ut_buttons.h" - "..\mock_board\mock_board_din.h" - "..\mock_board\mock_board_dout.h" - "..\..\src\hw\buttons.h" - -1699298688 source:f:\microrally\udccd_controller\tests\ut_hw\ut_led_display.c - "ut_led_display.h" - "..\mock_board\mock_board_odout.h" - "..\..\src\hw\led_display.h" - -1699298688 f:\microrally\udccd_controller\tests\ut_hw\ut_led_display.h - - - -1699298688 source:f:\microrally\udccd_controller\tests\ut_hw\ut_startup.c - "ut_startup.h" - "..\mock_board\mock_board_setup.h" - "..\..\src\hw\startup.h" - -1699391649 source:f:\microrally\udccd_controller\src\hw\hb_control.c - "board/utils/utils.h" - "board/halfbridge.h" - "hb_control.h" - -1699382733 f:\microrally\udccd_controller\src\hw\hb_control.h - - "board/utils/faults.h" - "board/utils/fuses.h" - "board/halfbridge.h" - -1699393539 source:f:\microrally\udccd_controller\tests\ut_hw\ut_hb_control.c - "ut_hb_control.h" - "..\mock_board\mock_board_halfbridge.h" - "..\..\src\hw\hb_control.h" - -1699382733 f:\microrally\udccd_controller\tests\ut_hw\ut_hb_control.h - - - -1699535389 d:\microrally\udccd_controller\src\hw\board\config.h - - -1699535687 d:\microrally\udccd_controller\src\hw\config.h - - diff --git a/firmware/tests/uDCCD_Unit_Tests_HW.layout b/firmware/tests/uDCCD_Unit_Tests_HW.layout deleted file mode 100644 index 22a15ce..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_HW.layout +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/firmware/tests/uDCCD_Unit_Tests_Logic.cbp b/firmware/tests/uDCCD_Unit_Tests_Logic.cbp deleted file mode 100644 index cf04a15..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_Logic.cbp +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - diff --git a/firmware/tests/uDCCD_Unit_Tests_Logic.depend b/firmware/tests/uDCCD_Unit_Tests_Logic.depend deleted file mode 100644 index b36d4bb..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_Logic.depend +++ /dev/null @@ -1,80 +0,0 @@ -# depslib dependency file v1.0 -1699474847 source:f:\microrally\udccd_controller\src\logic\coil.c - "coil.h" - -1699474848 f:\microrally\udccd_controller\src\logic\coil.h - - -1699472530 source:f:\microrally\udccd_controller\src\logic\display.c - "display.h" - -1699472535 f:\microrally\udccd_controller\src\logic\display.h - - -1699474995 source:f:\microrally\udccd_controller\src\logic\force.c - "force.h" - -1699474996 f:\microrally\udccd_controller\src\logic\force.h - - -1699031869 source:f:\microrally\udccd_controller\src\logic\pot.c - "pot.h" - -1699475345 f:\microrally\udccd_controller\src\logic\pot.h - - -1699475089 source:f:\microrally\udccd_controller\src\logic\user_force.c - "user_force.h" - -1699475337 f:\microrally\udccd_controller\src\logic\user_force.h - - -1699474661 source:f:\microrally\udccd_controller\tests\logic_main.c - - - "ut_logic/ut_coil.h" - "ut_logic/ut_display.h" - "ut_logic/ut_force.h" - "ut_logic/ut_pot.h" - "ut_logic/ut_user_force.h" - -1698869680 f:\microrally\udccd_controller\tests\ut_logic\ut_coil.h - - - -1699473947 f:\microrally\udccd_controller\tests\ut_logic\ut_display.h - - - -1698869680 f:\microrally\udccd_controller\tests\ut_logic\ut_force.h - - - -1698869680 f:\microrally\udccd_controller\tests\ut_logic\ut_pot.h - - - -1698869680 f:\microrally\udccd_controller\tests\ut_logic\ut_user_force.h - - - -1699474942 source:f:\microrally\udccd_controller\tests\ut_logic\ut_coil.c - "ut_coil.h" - "..\..\src\logic\coil.h" - -1699474733 source:f:\microrally\udccd_controller\tests\ut_logic\ut_display.c - "ut_display.h" - "..\..\src\logic\display.h" - -1699475023 source:f:\microrally\udccd_controller\tests\ut_logic\ut_force.c - "ut_force.h" - "..\..\src\logic\force.h" - -1699195810 source:f:\microrally\udccd_controller\tests\ut_logic\ut_pot.c - "ut_pot.h" - "..\..\src\logic\pot.h" - -1699195775 source:f:\microrally\udccd_controller\tests\ut_logic\ut_user_force.c - "ut_user_force.h" - "..\..\src\logic\user_force.h" - diff --git a/firmware/tests/uDCCD_Unit_Tests_Logic.layout b/firmware/tests/uDCCD_Unit_Tests_Logic.layout deleted file mode 100644 index 3b6ecd4..0000000 --- a/firmware/tests/uDCCD_Unit_Tests_Logic.layout +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/firmware/tests/ut_board/ut_ain.c b/firmware/tests/ut_board/ut_ain.c deleted file mode 100644 index 3568ea3..0000000 --- a/firmware/tests/ut_board/ut_ain.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "ut_ain.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\ain.h" - -static const uint8_t NOT_ACCESED_ADC_CH = 255; - -static int ut_bsp_ain_read(uint8_t ain_ch, uint16_t adc_raw, uint16_t exp_out, uint8_t exp_adc_ch) -{ - printf(" Input: Ain-Ch:%d Adc-Raw:%d \n", ain_ch, adc_raw); - - mock_mcu_adc_set_ch(NOT_ACCESED_ADC_CH); - mock_mcu_adc_set_raw(adc_raw, exp_adc_ch); - - uint16_t out = bsp_ain_read(ain_ch); - - uint8_t adc_ch = mock_mcu_adc_read_ch(); - - printf(" Output: AIN:%d Adc-Ch:%d \n", out, adc_ch); - printf("Expected: AIN:%d Adc-Ch:%d \n", exp_out, exp_adc_ch); - - if((out==exp_out)&&(adc_ch==exp_adc_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_ain_read_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t bsp_ain_read(uint8_t ch)\n"); - - int test_res; - int pass = 1; - - uint8_t ain_ch; - uint16_t adc_raw; - uint16_t exp_out; - uint8_t exp_adc_ch; - - // Normal 1 - ain_ch = BSP_AIN1; - adc_raw = 88; - exp_out = 430; - exp_adc_ch = MCU_ADC5; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - // Normal 2 - ain_ch = BSP_AIN2; - adc_raw = 88; - exp_out = 430; - exp_adc_ch = MCU_ADC4; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - // Normal 3 - ain_ch = BSP_AIN3; - adc_raw = 500; - exp_out = 2443; - exp_adc_ch = MCU_ADC8; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - // Norma 4 - ain_ch = BSP_AIN4; - adc_raw = 1023; - exp_out = 4998; - exp_adc_ch = MCU_ADC14; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - // Normal 5 - ain_ch = BSP_AIN5; - adc_raw = 1; - exp_out = 4; - exp_adc_ch = MCU_ADC15; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - // Not existing ADC channel - ain_ch = 0; - adc_raw = 500; - exp_out = 0; - exp_adc_ch = NOT_ACCESED_ADC_CH; - test_res = ut_bsp_ain_read(ain_ch, adc_raw, exp_out, exp_adc_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_board/ut_ain.h b/firmware/tests/ut_board/ut_ain.h deleted file mode 100644 index 6ff046e..0000000 --- a/firmware/tests/ut_board/ut_ain.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_BOARD_AIN_H_ -#define UT_BOARD_AIN_H_ - -#include -#include - -int ut_bsp_ain_read_test(void); - -#endif /* UT_BOARD_AIN_H_ */ diff --git a/firmware/tests/ut_board/ut_din.c b/firmware/tests/ut_board/ut_din.c deleted file mode 100644 index d55551b..0000000 --- a/firmware/tests/ut_board/ut_din.c +++ /dev/null @@ -1,191 +0,0 @@ -#include "ut_din.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\din.h" - -static const uint8_t NOT_ACCESED_GPIO_CH = 255; - -static int ut_bsp_din_read(uint8_t din_ch, uint8_t gpio_lvl, uint8_t exp_out, uint8_t exp_gpio_ch) -{ - printf(" Input: Din-Ch:%d GPIO-lvl:%d \n", din_ch, gpio_lvl); - - mock_mcu_gpio_set_ch(NOT_ACCESED_GPIO_CH); - mock_mcu_gpio_set_input_lvl(gpio_lvl, exp_gpio_ch); - - uint8_t out = bsp_din_read(din_ch); - - uint8_t gpio_ch = mock_mcu_gpio_read_ch(); - - printf(" Output: DIN:%d GPIO-Ch:%d \n", out, gpio_ch); - printf("Expected: DIN:%d GPIO-Ch:%d \n", exp_out, exp_gpio_ch); - - if((out==exp_out)&&(gpio_ch==exp_gpio_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_din_read_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t bsp_din_read(uint8_t ch)\n"); - - int test_res; - int pass = 1; - - uint8_t din_ch; - uint8_t gpio_lvl; - uint8_t exp_out; - uint8_t exp_gpio_ch; - - // DIN 1 - din_ch = BSP_DIN1; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN1; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 1; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 2 - din_ch = BSP_DIN2; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = MCU_GPIO1; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN2; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 1; - exp_gpio_ch = MCU_GPIO1; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 3 - din_ch = BSP_DIN3; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = MCU_GPIO2; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN3; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 1; - exp_gpio_ch = MCU_GPIO2; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 4 - din_ch = BSP_DIN4; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = MCU_GPIO3; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN4; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 1; - exp_gpio_ch = MCU_GPIO3; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 5 - din_ch = BSP_DIN5; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 1; - exp_gpio_ch = MCU_GPIO4; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN5; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 0; - exp_gpio_ch = MCU_GPIO4; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 6 - din_ch = BSP_DIN6; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 1; - exp_gpio_ch = MCU_GPIO5; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN6; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 0; - exp_gpio_ch = MCU_GPIO5; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 7 - din_ch = BSP_DIN7; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 1; - exp_gpio_ch = MCU_GPIO6; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN7; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 0; - exp_gpio_ch = MCU_GPIO6; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DIN 7N - din_ch = BSP_DIN7N; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = MCU_GPIO7; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN7N; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = 1; - exp_gpio_ch = MCU_GPIO7; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // Wrong channel - din_ch = 0; - gpio_lvl = MCU_GPIO_LOW; - exp_out = 0; - exp_gpio_ch = NOT_ACCESED_GPIO_CH; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - // DSP OUTPUT DEFINITIONS - din_ch = BSP_DIN1; - gpio_lvl = MCU_GPIO_LOW; - exp_out = BSP_DIN_LOW; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - din_ch = BSP_DIN1; - gpio_lvl = MCU_GPIO_HIGH; - exp_out = BSP_DIN_HIGH; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_din_read(din_ch, gpio_lvl, exp_out, exp_gpio_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_board/ut_din.h b/firmware/tests/ut_board/ut_din.h deleted file mode 100644 index 970513c..0000000 --- a/firmware/tests/ut_board/ut_din.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_BOARD_DIN_H_ -#define UT_BOARD_DIN_H_ - -#include -#include - -int ut_bsp_din_read_test(void); - -#endif /* UT_BOARD_DIN_H_ */ diff --git a/firmware/tests/ut_board/ut_dout.c b/firmware/tests/ut_board/ut_dout.c deleted file mode 100644 index 294336f..0000000 --- a/firmware/tests/ut_board/ut_dout.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "ut_dout.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\dout.h" - -static const uint8_t NOT_ACCESED_GPIO_CH = 255; -static const uint8_t NOT_ACCESED_GPIO_LVL = MCU_GPIO_HIZ; - -static int ut_bsp_dout_write(uint8_t dout_ch, int8_t dout_lvl, int8_t exp_gpio_lvl, uint8_t exp_gpio_ch) -{ - printf(" Input: Dout-Ch:%d Dout-lvl:%d \n", dout_ch, dout_lvl); - - mock_mcu_gpio_set_ch(NOT_ACCESED_GPIO_CH); - mock_mcu_gpio_set_output_lvl(NOT_ACCESED_GPIO_LVL, exp_gpio_ch); - - bsp_dout_write(dout_ch, dout_lvl); - - uint8_t gpio_ch = mock_mcu_gpio_read_ch(); - int8_t gpio_lvl = mock_mcu_gpio_read_output_lvl(gpio_ch); - - printf(" Output: GPIO-lvl:%d GPIO-Ch:%d \n", gpio_lvl, gpio_ch); - printf("Expected: GPIO-lvl:%d GPIO-Ch:%d \n", exp_gpio_lvl, exp_gpio_ch); - - if((gpio_lvl==exp_gpio_lvl)&&(gpio_ch==exp_gpio_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_dout_write_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_dout_write(uint8_t ch, int8_t lvl)\n"); - - int test_res; - int pass = 1; - - uint8_t dout_ch; - uint8_t dout_lvl; - int8_t exp_gpio_lvl; - uint8_t exp_gpio_ch; - - // DOUT 1 - dout_ch = BSP_DOUT1; - dout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - dout_ch = BSP_DOUT1; - dout_lvl = 1; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - dout_ch = BSP_DOUT1; - dout_lvl = -1; - exp_gpio_lvl = MCU_GPIO_HIZ; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Defines test - dout_ch = BSP_DOUT1; - dout_lvl = BSP_DOUT_LOW; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - dout_ch = BSP_DOUT1; - dout_lvl = BSP_DOUT_HIGH; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - dout_ch = BSP_DOUT1; - dout_lvl = BSP_DOUT_HIZ; - exp_gpio_lvl = MCU_GPIO_HIZ; - exp_gpio_ch = MCU_GPIO0; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // DOUT 2 - dout_ch = BSP_DOUT2; - dout_lvl = BSP_DOUT_LOW; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO1; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // DOUT 3 - dout_ch = BSP_DOUT3; - dout_lvl = BSP_DOUT_HIGH; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO2; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // DOUT 4 - dout_ch = BSP_DOUT4; - dout_lvl = BSP_DOUT_LOW; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO3; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // DOUT 5 - dout_ch = BSP_DOUT5; - dout_lvl = BSP_DOUT_HIGH; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO7; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // DOUT 6 - dout_ch = BSP_DOUT6; - dout_lvl = BSP_DOUT_LOW; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO8; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Wrong channel - dout_ch = 0; - dout_lvl = BSP_DOUT_HIGH; - exp_gpio_lvl = NOT_ACCESED_GPIO_LVL; - exp_gpio_ch = NOT_ACCESED_GPIO_CH; - test_res = ut_bsp_dout_write(dout_ch, dout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_board/ut_dout.h b/firmware/tests/ut_board/ut_dout.h deleted file mode 100644 index 7b6108f..0000000 --- a/firmware/tests/ut_board/ut_dout.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_BOARD_DOUT_H_ -#define UT_BOARD_DOUT_H_ - -#include -#include - -int ut_bsp_dout_write_test(void); - -#endif /* UT_BOARD_DOUT_H_ */ diff --git a/firmware/tests/ut_board/ut_halfbridge.c b/firmware/tests/ut_board/ut_halfbridge.c deleted file mode 100644 index 4116b76..0000000 --- a/firmware/tests/ut_board/ut_halfbridge.c +++ /dev/null @@ -1,273 +0,0 @@ -#include "ut_dout.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\halfbridge.h" - -static const uint8_t NOT_ACCESED_GPIO_CH = 255; -static const uint8_t NOT_ACCESED_GPIO_LVL = MCU_GPIO_HIZ; - -static int ut_bsp_hb_write_low(uint8_t state, int8_t exp_gpio_lvl, uint8_t exp_gpio_ch) -{ - printf(" Input: State:%d \n", state); - - mock_mcu_gpio_set_ch(NOT_ACCESED_GPIO_CH); - mock_mcu_gpio_set_output_lvl(NOT_ACCESED_GPIO_LVL, exp_gpio_ch); - - bsp_hb_write_low(state); - - uint8_t gpio_ch = mock_mcu_gpio_read_ch(); - int8_t gpio_lvl = mock_mcu_gpio_read_output_lvl(gpio_ch); - - printf(" Output: GPIO-lvl:%d GPIO-Ch:%d \n", gpio_lvl, gpio_ch); - printf("Expected: GPIO-lvl:%d GPIO-Ch:%d \n", exp_gpio_lvl, exp_gpio_ch); - - if((gpio_lvl==exp_gpio_lvl)&&(gpio_ch==exp_gpio_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_hb_write_low_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_hb_write_low(uint8_t state) \n"); - - int test_res; - int pass = 1; - - uint8_t state; - int8_t exp_gpio_lvl; - uint8_t exp_gpio_ch; - - // Low - state = 0; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO15; - test_res = ut_bsp_hb_write_low(state, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // High - state = 1; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO15; - test_res = ut_bsp_hb_write_low(state, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // High - state = 255; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO15; - test_res = ut_bsp_hb_write_low(state, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - return pass; -} - -static const uint8_t NOT_ACCESED_PWM_CH = 255; -static const uint8_t NOT_ACCESED_PWM_VAL = 0xFFFF; - -static int ut_bsp_hb_write_pwm(uint16_t pwm, uint16_t exp_pwm, uint8_t exp_pwm_ch) -{ - printf(" Input: PWM:%d \n", pwm); - - mock_mcu_pwm_set_ch(NOT_ACCESED_PWM_CH); - mock_mcu_pwm_set_raw(NOT_ACCESED_PWM_VAL, exp_pwm_ch); - - bsp_hb_write_pwm(pwm); - - uint8_t pwm_ch = mock_mcu_pwm_read_ch(); - uint16_t pwm_value = mock_mcu_pwm_read_raw(pwm_ch); - - printf(" Output: PWM:%d PWM-Ch:%d \n", pwm_value, pwm_ch); - printf("Expected: PWM:%d PWM-Ch:%d \n", exp_pwm, exp_pwm_ch); - - if((pwm_value==exp_pwm)&&(pwm_ch==exp_pwm_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_hb_write_pwm_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_hb_write_pwm(uint16_t pwm) \n"); - - int test_res; - int pass = 1; - - uint16_t pwm; - uint16_t exp_pwm; - uint8_t exp_pwm_ch; - - // 0 - pwm = 0; - exp_pwm = 0; - exp_pwm_ch = MCU_PWM0; - test_res = ut_bsp_hb_write_pwm(pwm, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - // 100 - pwm = 100; - exp_pwm = 100; - exp_pwm_ch = MCU_PWM0; - test_res = ut_bsp_hb_write_pwm(pwm, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - // 95% - pwm = 0xFC00; - exp_pwm = 0xFC00; - exp_pwm_ch = MCU_PWM0; - test_res = ut_bsp_hb_write_pwm(pwm, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - // MAX - pwm = 0xFFFF; - exp_pwm = 0xFC00; - exp_pwm_ch = MCU_PWM0; - test_res = ut_bsp_hb_write_pwm(pwm, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - return pass; -} - -static const uint8_t NOT_ACCESED_ADC_CH = 255; -static const uint8_t NOT_ACCESED_ADC_VAL = 0xFFFF; - -static int ut_bsp_hb_read_meas(uint16_t adc_sup_u, uint16_t adc_sup_i, uint16_t adc_out_u, uint16_t adc_out_i, uint8_t low_side_lvl, uint16_t act_pwm, hb_meas_t* exp_meas) -{ - printf(" Input: Out-U:%d Out-I:%d Sup-U:%d Sup-I:%d Low-Lvl:%d PWM:%d \n", adc_out_u, adc_out_i, adc_sup_u, adc_sup_i, low_side_lvl, act_pwm); - - mock_mcu_adc_set_ch(NOT_ACCESED_ADC_CH); - mock_mcu_adc_set_raw(adc_sup_u, MCU_ADC2); - mock_mcu_adc_set_raw(adc_sup_i, MCU_ADC3); - mock_mcu_adc_set_raw(adc_out_u, MCU_ADC1); - mock_mcu_adc_set_raw(adc_out_i, MCU_ADC0); - - mock_mcu_gpio_set_ch(NOT_ACCESED_GPIO_CH); - mock_mcu_gpio_set_input_lvl(low_side_lvl, MCU_GPIO15); - - mock_mcu_pwm_set_ch(NOT_ACCESED_PWM_CH); - mock_mcu_pwm_set_raw(act_pwm, MCU_PWM0); - - hb_meas_t out; - - bsp_hb_read_meas(&out); - - uint8_t adc_ch = mock_mcu_adc_read_ch(); - uint8_t gpio_ch = mock_mcu_gpio_read_ch(); - uint8_t pwm_ch = mock_mcu_pwm_read_ch(); - - int equal = 1; - - if(out.out_voltage != exp_meas->out_voltage) equal = 0; - if(out.out_current != exp_meas->out_current) equal = 0; - if(out.sup_voltage != exp_meas->sup_voltage) equal = 0; - if(out.sup_current != exp_meas->sup_current) equal = 0; - if(out.out_power != exp_meas->out_power) equal = 0; - if(out.sup_power != exp_meas->sup_power) equal = 0; - if(out.out_impedance != exp_meas->out_impedance) equal = 0; - if(out.low_side_ctrl != exp_meas->low_side_ctrl) equal = 0; - if(out.pwm != exp_meas->pwm) equal = 0; - - printf(" Output: Out-U:%d Out-I:%d Bat-U:%d Bat-I:%d Out-P:%d Bat-P:%d Out-Z:%d Low:%d PWM:%d \n", out.out_voltage, out.out_current, out.sup_voltage, out.sup_current, out.out_power, out.sup_power, out.out_impedance, out.low_side_ctrl, out.pwm); - printf("Expected: Out-U:%d Out-I:%d Bat-U:%d Bat-I:%d Out-P:%d Bat-P:%d Out-Z:%d Low:%d PWM:%d \n", exp_meas->out_voltage, exp_meas->out_current, exp_meas->sup_voltage, exp_meas->sup_current, exp_meas->out_power, exp_meas->sup_power, exp_meas->out_impedance, exp_meas->low_side_ctrl, exp_meas->pwm); - - if((equal)&&(gpio_ch==MCU_GPIO15)&&(pwm_ch==MCU_PWM0)&&(adc_ch!=NOT_ACCESED_ADC_CH)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_hb_read_meas_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_hb_read_meas(hb_meas_t* measurements) \n"); - - int test_res; - int pass = 1; - - uint16_t adc_sup_u; - uint16_t adc_sup_i; - uint16_t adc_out_u; - uint16_t adc_out_i; - uint8_t low_side_lvl; - uint16_t act_pwm; - hb_meas_t exp_meas; - - // 0 - adc_sup_u = 0; - adc_sup_i = 0; - adc_out_u = 0; - adc_out_i = 0; - low_side_lvl = MCU_GPIO_LOW; - act_pwm = 0x0000; - exp_meas.out_voltage = 0; - exp_meas.out_current = 0; - exp_meas.sup_voltage = 0; - exp_meas.sup_current = 0; - exp_meas.out_power = 0; - exp_meas.sup_power = 0; - exp_meas.out_impedance = 0xFFFF; - exp_meas.low_side_ctrl = 0; - exp_meas.pwm = 0; - test_res = ut_bsp_hb_read_meas(adc_sup_u, adc_sup_i, adc_out_u, adc_out_i, low_side_lvl, act_pwm, &exp_meas); - if(!test_res) pass = 0; - - // Limits - adc_sup_u = 1023; - adc_sup_i = 1023; - adc_out_u = 1023; - adc_out_i = 1023; - low_side_lvl = MCU_GPIO_HIGH; - act_pwm = 457; - exp_meas.out_voltage = 20460; - exp_meas.out_current = 9997; - exp_meas.sup_voltage = 20460; - exp_meas.sup_current = 40067; - exp_meas.out_power = 0xFFFF; - exp_meas.sup_power = 0xFFFF; - exp_meas.out_impedance = 2046; - exp_meas.low_side_ctrl = 1; - exp_meas.pwm = 457; - test_res = ut_bsp_hb_read_meas(adc_sup_u, adc_sup_i, adc_out_u, adc_out_i, low_side_lvl, act_pwm, &exp_meas); - if(!test_res) pass = 0; - - // Normal - adc_sup_u = 600; - adc_sup_i = 51; - adc_out_u = 300; - adc_out_i = 409; - low_side_lvl = MCU_GPIO_HIGH; - act_pwm = 0xAAAA; - exp_meas.out_voltage = 6000; - exp_meas.out_current = 3997; - exp_meas.sup_voltage = 12000; - exp_meas.sup_current = 1997; - exp_meas.out_power = 23982; - exp_meas.sup_power = 23964; - exp_meas.out_impedance = 1501; - exp_meas.low_side_ctrl = 1; - exp_meas.pwm = 0xAAAA; - test_res = ut_bsp_hb_read_meas(adc_sup_u, adc_sup_i, adc_out_u, adc_out_i, low_side_lvl, act_pwm, &exp_meas); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_board/ut_halfbridge.h b/firmware/tests/ut_board/ut_halfbridge.h deleted file mode 100644 index 632697e..0000000 --- a/firmware/tests/ut_board/ut_halfbridge.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef UT_BOARD_HALFBRIDGE_H_ -#define UT_BOARD_HALFBRIDGE_H_ - -#include -#include - -int ut_bsp_hb_write_low_test(void); -int ut_bsp_hb_write_pwm_test(void); -int ut_bsp_hb_read_meas_test(void); - -#endif /* UT_BOARD_HALFBRIDGE_H_ */ diff --git a/firmware/tests/ut_board/ut_odout.c b/firmware/tests/ut_board/ut_odout.c deleted file mode 100644 index 9379742..0000000 --- a/firmware/tests/ut_board/ut_odout.c +++ /dev/null @@ -1,212 +0,0 @@ -#include "ut_odout.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\odout.h" - -static const uint8_t NOT_ACCESED_GPIO_CH = 255; -static const uint8_t NOT_ACCESED_GPIO_LVL = MCU_GPIO_HIZ; - -static int ut_bsp_odout_write(uint8_t odout_ch, int8_t odout_lvl, int8_t exp_gpio_lvl, uint8_t exp_gpio_ch) -{ - printf(" Input: ODout-Ch:%d ODout-lvl:%d \n", odout_ch, odout_lvl); - - mock_mcu_gpio_set_ch(NOT_ACCESED_GPIO_CH); - mock_mcu_gpio_set_output_lvl(NOT_ACCESED_GPIO_LVL, exp_gpio_ch); - - bsp_odout_write(odout_ch, odout_lvl); - - uint8_t gpio_ch = mock_mcu_gpio_read_ch(); - int8_t gpio_lvl = mock_mcu_gpio_read_output_lvl(gpio_ch); - - printf(" Output: GPIO-lvl:%d GPIO-Ch:%d \n", gpio_lvl, gpio_ch); - printf("Expected: GPIO-lvl:%d GPIO-Ch:%d \n", exp_gpio_lvl, exp_gpio_ch); - - if((gpio_lvl==exp_gpio_lvl)&&(gpio_ch==exp_gpio_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_odout_write_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_odout_write(uint8_t ch, int8_t lvl) \n"); - - int test_res; - int pass = 1; - - uint8_t odout_ch; - uint8_t odout_lvl; - int8_t exp_gpio_lvl; - uint8_t exp_gpio_ch; - - // Control level - LOW - odout_ch = BSP_OD1; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Control level - HIGH - odout_ch = BSP_OD1; - odout_lvl = 1; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Control level - HIZ - odout_ch = BSP_OD1; - odout_lvl = -1; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Control level DEFINE - LOW - odout_ch = BSP_OD1; - odout_lvl = BSP_ODOUT_LOW; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Control level DEFINE - HIGH - odout_ch = BSP_OD1; - odout_lvl = BSP_ODOUT_HIGH; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Control level DEFINE - HIZ - odout_ch = BSP_OD1; - odout_lvl = BSP_ODOUT_HIZ; - exp_gpio_lvl = MCU_GPIO_LOW; - exp_gpio_ch = MCU_GPIO9; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // ODOUT 2 - odout_ch = BSP_OD2; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO10; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // ODOUT 3 - odout_ch = BSP_OD3; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO11; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // ODOUT 4 - odout_ch = BSP_OD4; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO12; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // ODOUT 5 - odout_ch = BSP_OD5; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO13; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // ODOUT 6 - odout_ch = BSP_OD6; - odout_lvl = 0; - exp_gpio_lvl = MCU_GPIO_HIGH; - exp_gpio_ch = MCU_GPIO14; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - // Wrong chanell - odout_ch = 0; - odout_lvl = 0; - exp_gpio_lvl = NOT_ACCESED_GPIO_LVL; - exp_gpio_ch = NOT_ACCESED_GPIO_CH; - test_res = ut_bsp_odout_write(odout_ch, odout_lvl, exp_gpio_lvl, exp_gpio_ch); - if(!test_res) pass = 0; - - return pass; -} - -static const uint8_t NOT_ACCESED_PWM_CH = 255; -static const uint8_t NOT_ACCESED_PWM_VAL = 0xFFFF; - -static int ut_bsp_odout_write_common(uint8_t percent, uint16_t exp_pwm, uint8_t exp_pwm_ch) -{ - printf(" Input: Percent:%d \n", percent); - - mock_mcu_pwm_set_ch(NOT_ACCESED_PWM_CH); - mock_mcu_pwm_set_raw(NOT_ACCESED_PWM_VAL, exp_pwm_ch); - - bsp_odout_write_common(percent); - - uint8_t pwm_ch = mock_mcu_pwm_read_ch(); - uint16_t pwm_value = mock_mcu_pwm_read_raw(pwm_ch); - - printf(" Output: PWM:%d PWM-Ch:%d \n", pwm_value, pwm_ch); - printf("Expected: PWM:%d PWM-Ch:%d \n", exp_pwm, exp_pwm_ch); - - if((pwm_value==exp_pwm)&&(pwm_ch==exp_pwm_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_odout_write_common_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_odout_write_common(uint8_t percent) \n"); - - int test_res; - int pass = 1; - - uint8_t percent; - uint16_t exp_pwm; - uint8_t exp_pwm_ch; - - // Zero - percent = 0; - exp_pwm = 0x0000; - exp_pwm_ch = MCU_PWM1; - test_res = ut_bsp_odout_write_common(percent, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - // 100 - percent = 100; - exp_pwm = 0xFFFF; - exp_pwm_ch = MCU_PWM1; - test_res = ut_bsp_odout_write_common(percent, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - // 50 - percent = 50; - exp_pwm = 0x7FFF; - exp_pwm_ch = MCU_PWM1; - test_res = ut_bsp_odout_write_common(percent, exp_pwm, exp_pwm_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_board/ut_odout.h b/firmware/tests/ut_board/ut_odout.h deleted file mode 100644 index 4905b2f..0000000 --- a/firmware/tests/ut_board/ut_odout.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UT_BOARD_ODOUT_H_ -#define UT_BOARD_ODOUT_H_ - -#include -#include - -int ut_bsp_odout_write_test(void); -int ut_bsp_odout_write_common_test(void); - -#endif /* UT_BOARD_ODOUT_H_ */ diff --git a/firmware/tests/ut_board/ut_setup.c b/firmware/tests/ut_board/ut_setup.c deleted file mode 100644 index f15aa37..0000000 --- a/firmware/tests/ut_board/ut_setup.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "ut_odout.h" - -#include "..\mock_mcu\mock_mcu_hal_r8.h" -#include "..\..\src\hw\board\setup.h" - -static adcClkDiv_t not_accesed_adc_clk = MCU_ADC_DIV128; -static timerClkDiv_t not_accesed_pwm_clk = MCU_TIM_DIV1024; -static uint16_t not_accesed_pwm_top = 0; -static uint8_t not_accesed_pwm_chb_en = 255; - -static int ut_bsp_startup(startupCfg_t* exp_cfg) -{ - printf(" Input: \n"); - - startupCfg_t not_accesd_cfg; - not_accesd_cfg.adc_clk = not_accesed_adc_clk; - not_accesd_cfg.pwm_clk = not_accesed_pwm_clk; - not_accesd_cfg.pwm_top = not_accesed_pwm_top; - not_accesd_cfg.pwm_chb_en = not_accesed_pwm_chb_en; - mock_mcu_startup_write_cfg(¬_accesd_cfg); - - bsp_startup(); - - startupCfg_t cfg; - mock_mcu_startup_read_cfg(&cfg); - - printf(" Output: ADC-CLK:%d PWN-CLK:%d PWM-TOP:%d PWM-CHB-EN:%d \n", cfg.adc_clk, cfg.pwm_clk, cfg.pwm_top, cfg.pwm_chb_en); - printf("Expected: ADC-CLK:%d PWN-CLK:%d PWM-TOP:%d PWM-CHB-EN:%d \n", exp_cfg->adc_clk, exp_cfg->pwm_clk, exp_cfg->pwm_top, exp_cfg->pwm_chb_en); - - if((cfg.adc_clk==exp_cfg->adc_clk)&&(cfg.pwm_clk==exp_cfg->pwm_clk)&&(cfg.pwm_top==exp_cfg->pwm_top)&&(cfg.pwm_chb_en==exp_cfg->pwm_chb_en)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_bsp_startup_test(void) -{ - printf("******************************************************\n"); - printf("void bsp_startup(void) \n"); - - int test_res; - int pass = 1; - - startupCfg_t exp_cfg; - - // Control level - LOW - exp_cfg.adc_clk = MCU_ADC_DIV2; - exp_cfg.pwm_clk = MCU_TIM_DIV1; - exp_cfg.pwm_top = 511; - exp_cfg.pwm_chb_en = 1; - test_res = ut_bsp_startup(&exp_cfg); - if(!test_res) pass = 0; - - return pass; -} - diff --git a/firmware/tests/ut_board/ut_setup.h b/firmware/tests/ut_board/ut_setup.h deleted file mode 100644 index 0957e3b..0000000 --- a/firmware/tests/ut_board/ut_setup.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_BOARD_SETUP_H_ -#define UT_BOARD_SETUP_H_ - -#include -#include - -int ut_bsp_startup_test(void); - -#endif /* UT_BOARD_SETUP_H_ */ diff --git a/firmware/tests/ut_hw/ut_analog.c b/firmware/tests/ut_hw/ut_analog.c deleted file mode 100644 index 2ae20ac..0000000 --- a/firmware/tests/ut_hw/ut_analog.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "ut_analog.h" - -#include "..\mock_board\mock_board_ain.h" - -#include "..\..\src\hw\analog.h" - -static const uint8_t NOT_ACCESED_AIN_CH = 255; - -static int ut_analog_ch_get(uint8_t analog_ch, uint16_t ain_value, uint16_t exp_out, uint8_t exp_ain_ch) -{ - printf(" Input: Analog-Ch:%d AIN-Value:%d \n", analog_ch, ain_value); - - mock_board_ain_write_ch(NOT_ACCESED_AIN_CH); - mock_board_ain_write_data(exp_ain_ch, ain_value); - - uint16_t out = analog_ch_get(analog_ch); - - uint8_t ain_ch = mock_board_ain_read_ch(); - - printf(" Output: ANALOG:%d AIN-Ch:%d \n", out, ain_ch); - printf("Expected: ANALOG:%d AIN-Ch:%d \n", exp_out, exp_ain_ch); - - if((out==exp_out)&&(ain_ch==exp_ain_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_analog_ch_get_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t analog_ch_get(uint8_t analog_ch) \n"); - - int test_res; - int pass = 1; - - uint8_t analog_ch; - uint16_t ain_value; - uint16_t exp_out; - uint8_t exp_ain_ch; - - // Normal 1 - analog_ch = ANALOG_1; - ain_value = 2500; - exp_out = 2500; - exp_ain_ch = BSP_AIN2; - test_res = ut_analog_ch_get(analog_ch, ain_value, exp_out, exp_ain_ch); - if(!test_res) pass = 0; - - // Normal 2 - analog_ch = ANALOG_2; - ain_value = 3000; - exp_out = 3000; - exp_ain_ch = BSP_AIN1; - test_res = ut_analog_ch_get(analog_ch, ain_value, exp_out, exp_ain_ch); - if(!test_res) pass = 0; - - // Wrong channel - analog_ch = 0; - ain_value = 1200; - exp_out = 0; - exp_ain_ch = NOT_ACCESED_AIN_CH; - test_res = ut_analog_ch_get(analog_ch, ain_value, exp_out, exp_ain_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_hw/ut_analog.h b/firmware/tests/ut_hw/ut_analog.h deleted file mode 100644 index fc3d064..0000000 --- a/firmware/tests/ut_hw/ut_analog.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_HW_ANALOG_H_ -#define UT_HW_ANALOG_H_ - -#include -#include - -int ut_analog_ch_get_test(void); - -#endif /* UT_HW_ANALOG_H_ */ diff --git a/firmware/tests/ut_hw/ut_buttons.c b/firmware/tests/ut_hw/ut_buttons.c deleted file mode 100644 index 7cebd6b..0000000 --- a/firmware/tests/ut_hw/ut_buttons.c +++ /dev/null @@ -1,854 +0,0 @@ -#include "ut_buttons.h" - -#include "..\mock_board\mock_board_din.h" -#include "..\mock_board\mock_board_dout.h" - -#include "..\..\src\hw\buttons.h" - -static const uint8_t NOT_ACCESED_DIN_CH = 255; -static const uint8_t NOT_ACCESED_DIN_LVL = BSP_DIN_LOW; - -static const uint8_t NOT_ACCESED_DOUT_CH = 255; -static const int8_t NOT_ACCESED_DOUT_LVL = BSP_DOUT_HIZ; - -static int ut_btn_reset(btn_t* btn, btn_t* exp_btn) -{ - printf(" Input: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - - btn_reset(btn); - - printf(" Output: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - printf("Expected: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", exp_btn->state, exp_btn->new_state, exp_btn->state_time, exp_btn->transition_cntr, exp_btn->dbnc_active); - - int equal = 1; - if(btn->state != exp_btn->state) equal = 0; - if(btn->new_state != exp_btn->new_state) equal = 0; - if(btn->state_time != exp_btn->state_time) equal = 0; - if(btn->transition_cntr != exp_btn->transition_cntr) equal = 0; - if(btn->dbnc_active != exp_btn->dbnc_active) equal = 0; - - if(equal) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_btn_reset_test(void) -{ - printf("******************************************************\n"); - printf("void btn_reset(btn_t* btn) \n"); - - int test_res; - int pass = 1; - - btn_t btn; - btn_t exp_btn; - - // Normal 1 - btn.state = 0; - btn.new_state = 0; - btn.state_time = 0; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 1; - test_res = ut_btn_reset(&btn, &exp_btn); - if(!test_res) pass = 0; - - // Normal 1 - btn.state = 1; - btn.new_state = 1; - btn.state_time = 1000; - btn.transition_cntr = 20; - btn.dbnc_active = 0; - exp_btn.state = BTN_INACTIVE; - exp_btn.new_state = 0; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 1; - test_res = ut_btn_reset(&btn, &exp_btn); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_btn_process(uint8_t lvl, btn_t* btn, btn_cfg_t* cfg, uint8_t exp_out, btn_t* exp_btn, btn_cfg_t* exp_cfg) -{ - printf(" Input: Level:%d \n", lvl); - printf(" Input: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - printf(" Input: Act-Lvl:%d Debounce-lim:%d Repeat-Time:%d \n", cfg->act_lvl, cfg->dbnc_lim, cfg->repeat_time); - - uint8_t out = btn_process(lvl, btn, cfg); - - printf(" Output: %d \n", out); - printf(" Output: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - printf(" Output: Act-Lvl:%d Debounce-lim:%d Repeat-Time:%d \n", cfg->act_lvl, cfg->dbnc_lim, cfg->repeat_time); - - printf("Expected: %d \n", exp_out); - printf("Expected: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", exp_btn->state, exp_btn->new_state, exp_btn->state_time, exp_btn->transition_cntr, exp_btn->dbnc_active); - printf("Expected: Act-Lvl:%d Debounce-lim:%d Repeat-Time:%d \n", exp_cfg->act_lvl, exp_cfg->dbnc_lim, exp_cfg->repeat_time); - - int equal = 1; - if(btn->state != exp_btn->state) equal = 0; - if(btn->new_state != exp_btn->new_state) equal = 0; - if(btn->state_time != exp_btn->state_time) equal = 0; - if(btn->transition_cntr != exp_btn->transition_cntr) equal = 0; - if(btn->dbnc_active != exp_btn->dbnc_active) equal = 0; - if(cfg->act_lvl != exp_cfg->act_lvl) equal = 0; - if(cfg->dbnc_lim != exp_cfg->dbnc_lim) equal = 0; - if(cfg->repeat_time != exp_cfg->repeat_time) equal = 0; - - if((out==exp_out)&&(equal)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_btn_process_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t btn_process(uint8_t lvl, btn_t* btn, btn_cfg_t* cfg) \n"); - - int test_res; - int pass = 1; - - uint8_t lvl; - btn_t btn; - btn_cfg_t cfg; - uint8_t exp_out; - btn_t exp_btn; - btn_cfg_t exp_cfg; - - // No changes - lvl = 0; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 0; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 1; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // State time increase - lvl = 1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 12345; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 0; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 12346; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Activation, first time, act high - lvl = 1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 38455; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Activation, first time, act low - lvl = 0; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 38455; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 0; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Active second time, not cleared new flag - lvl = 1; - btn.state = 1; - btn.new_state = 1; - btn.state_time = 0; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 1; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Active second time, cleared new flag - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 0; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 0; - exp_btn.state_time = 1; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Deactive, act high - lvl = 0; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 60584; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Deactive, act low - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 558; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 0; - cfg.dbnc_lim = 0; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Debounce, first - lvl = 1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 24789; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 24790; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 1; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Debounce at end - lvl = 1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 142; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 0; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Debounce fail, act high, fail to active state - lvl = 0; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 142; - btn.transition_cntr = 5; - btn.dbnc_active = 1; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 143; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Debounce fail, act low, fail to active state - lvl = 1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 257; - btn.transition_cntr = 4; - btn.dbnc_active = 1; - cfg.act_lvl = 0; - cfg.dbnc_lim = 10; - cfg.repeat_time = 0; - - exp_out = 0; - exp_btn.state = 0; - exp_btn.new_state = 0; - exp_btn.state_time = 258; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Before New state repeat - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 4998; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 5000; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 0; - exp_btn.state_time = 4999; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Before New state repeat - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 4999; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 5000; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 5000; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Before New state repeat - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 5000; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 5000; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 0; - exp_btn.state_time = 5001; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Before New state repeat - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 19999; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 5000; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 20000; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - // Before New state repeat - lvl = 1; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 0xFFFE; - btn.transition_cntr = 0; - btn.dbnc_active = 0; - cfg.act_lvl = 1; - cfg.dbnc_lim = 10; - cfg.repeat_time = 255; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 0; - exp_btn.state_time = 0xFFFF; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_cfg.act_lvl = cfg.act_lvl; - exp_cfg.dbnc_lim = cfg.dbnc_lim; - exp_cfg.repeat_time = cfg.repeat_time; - - test_res = ut_btn_process(lvl, &btn, &cfg, exp_out, &exp_btn, &exp_cfg); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_btn_ch_process(uint8_t btn_ch, btn_t* btn, uint8_t din_lvl, uint8_t exp_out, btn_t* exp_btn, uint8_t exp_din_ch) -{ - printf(" Input: Btn-Ch:%d Din-Lvl:%d\n", btn_ch, din_lvl); - printf(" Input: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - - mock_board_din_write_ch(NOT_ACCESED_DIN_CH); - mock_board_din_write_data(exp_din_ch, din_lvl); - - uint8_t out = btn_ch_process(btn_ch, btn); - - uint8_t din_ch = mock_board_din_read_ch(); - - printf(" Output: %d \n", out); - printf(" Output: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", btn->state, btn->new_state, btn->state_time, btn->transition_cntr, btn->dbnc_active); - printf(" Output: Din-Ch:%d \n", din_ch); - - printf("Expected: %d \n", exp_out); - printf("Expected: State:%d New-State:%d State-time:%d Transition-cntr:%d Debounce-act:%d \n", exp_btn->state, exp_btn->new_state, exp_btn->state_time, exp_btn->transition_cntr, exp_btn->dbnc_active); - printf("Expected: Din-Ch:%d \n", exp_din_ch); - - int equal = 1; - if(btn->state != exp_btn->state) equal = 0; - if(btn->new_state != exp_btn->new_state) equal = 0; - if(btn->state_time != exp_btn->state_time) equal = 0; - if(btn->transition_cntr != exp_btn->transition_cntr) equal = 0; - if(btn->dbnc_active != exp_btn->dbnc_active) equal = 0; - - if((out==exp_out)&&(equal)&&(din_ch==exp_din_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_btn_ch_process_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t btn_ch_process(uint8_t btn_ch, btn_t* btn) \n"); - - int test_res; - int pass = 1; - - uint8_t btn_ch; - btn_t btn; - uint8_t din_lvl; - uint8_t exp_out; - btn_t exp_btn; - uint8_t exp_din_ch; - - // Mapping check 1 - btn_ch = BTN_1; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN1; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 2 - btn_ch = BTN_2; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN3; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 3 - btn_ch = BTN_3; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN4; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 4 - btn_ch = BTN_4; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_HIGH; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN5; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 5 - btn_ch = BTN_5; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_HIGH; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN6; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 6 - btn_ch = BTN_6; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN7; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Mapping check 6N - btn_ch = BTN_6N; - btn.state = 0; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 1; - exp_btn.state = 1; - exp_btn.new_state = 1; - exp_btn.state_time = 0; - exp_btn.transition_cntr = 0; - exp_btn.dbnc_active = 0; - exp_din_ch = BSP_DIN7N; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - // Wrong channel - btn_ch = 0; - btn.state = 1; - btn.new_state = 0; - btn.state_time = 150; - btn.transition_cntr = 9; - btn.dbnc_active = 1; - din_lvl = BSP_DIN_LOW; - - exp_out = 0; - exp_btn.state = 1; - exp_btn.new_state = 0; - exp_btn.state_time = 150; - exp_btn.transition_cntr = 9; - exp_btn.dbnc_active = 1; - exp_din_ch = NOT_ACCESED_DIN_CH; - - test_res = ut_btn_ch_process(btn_ch, &btn, din_lvl, exp_out, &exp_btn, exp_din_ch); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_btn_ch_set_pull(uint8_t btn_ch, int8_t lvl, int8_t exp_dout_lvl, uint8_t exp_dout_ch) -{ - printf(" Input: Btn-Ch:%d Din-Lvl:%d\n", btn_ch, lvl); - - mock_board_dout_write_ch(NOT_ACCESED_DOUT_CH); - mock_board_dout_write_data(exp_dout_ch, NOT_ACCESED_DOUT_LVL); - - btn_ch_set_pull(btn_ch, lvl); - - uint8_t dout_ch = mock_board_dout_read_ch(); - int8_t dout = mock_board_dout_read_data(exp_dout_ch); - - printf(" Output: Dout-Ch:%d Dout-Lvl:%d \n", dout_ch, dout); - printf("Expected: Dout-Ch:%d Dout-Lvl:%d \n", exp_dout_ch, exp_dout_lvl); - - if((dout==exp_dout_lvl)&&(dout_ch==exp_dout_ch)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_btn_ch_set_pull_test(void) -{ - printf("******************************************************\n"); - printf("void btn_ch_set_pull(uint8_t btn_ch, int8_t lvl) \n"); - - int test_res; - int pass = 1; - - uint8_t btn_ch; - int8_t lvl; - int8_t exp_dout_lvl; - uint8_t exp_dout_ch; - - // Mapping check 1 - btn_ch = BTN_6; - lvl = 0; - exp_dout_lvl = BSP_DOUT_LOW; - exp_dout_ch = BSP_DOUT5; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - // Mapping check 2 - btn_ch = BTN_6N; - lvl = 1; - exp_dout_lvl = BSP_DOUT_HIGH; - exp_dout_ch = BSP_DOUT5; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - // Define check 1 - btn_ch = BTN_6N; - lvl = BTN_PULL_LOW; - exp_dout_lvl = BSP_DOUT_LOW; - exp_dout_ch = BSP_DOUT5; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - // Define check 2 - btn_ch = BTN_6N; - lvl = BTN_PULL_HIGH; - exp_dout_lvl = BSP_DOUT_HIGH; - exp_dout_ch = BSP_DOUT5; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - // Define check 3 - btn_ch = BTN_6N; - lvl = BTN_PULL_NONE; - exp_dout_lvl = BSP_DOUT_HIZ; - exp_dout_ch = BSP_DOUT5; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - // Wrong channel - btn_ch = 0; - lvl = 0; - exp_dout_lvl = NOT_ACCESED_DOUT_LVL; - exp_dout_ch = NOT_ACCESED_DOUT_CH; - test_res = ut_btn_ch_set_pull(btn_ch, lvl, exp_dout_lvl, exp_dout_ch); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_hw/ut_buttons.h b/firmware/tests/ut_hw/ut_buttons.h deleted file mode 100644 index dfc509d..0000000 --- a/firmware/tests/ut_hw/ut_buttons.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef UT_HW_BUTTONS_H_ -#define UT_HW_BUTTONS_H_ - -#include -#include - -int ut_btn_reset_test(void); -int ut_btn_process_test(void); -int ut_btn_ch_process_test(void); -int ut_btn_ch_set_pull_test(void); - -#endif /* UT_HW_BUTTONS_H_ */ diff --git a/firmware/tests/ut_hw/ut_hb_control.c b/firmware/tests/ut_hw/ut_hb_control.c deleted file mode 100644 index 8092595..0000000 --- a/firmware/tests/ut_hw/ut_hb_control.c +++ /dev/null @@ -1,2638 +0,0 @@ -#include "ut_hb_control.h" - -#include "..\mock_board\mock_board_halfbridge.h" - -#include "..\..\src\hw\hb_control.h" - -static void print_fb_struct(hb_feedback_t* f1) -{ - printf("Sup-U:%d Sup-I:%d Sup-P:%d \n", f1->analog.sup_voltage, f1->analog.sup_current, f1->analog.sup_power); - printf("Out-U:%d Out-I:%d Out-P:%d Out-Z:%d \n", f1->analog.out_voltage, f1->analog.out_current, f1->analog.out_power, f1->analog.out_impedance); - printf("Low:%d PWM:%d \n", f1->analog.low_side_ctrl, f1->analog.pwm); - printf("Enabled:%d Warnin:%d Fault:%d Fused:%d \n", f1->enabled, f1->warning_act, f1->fault_act, f1->fused); - printf("Sup-UVP:%d Sup-OVP:%d Sup-OCP:%d Sup-OPP:%d \n", f1->warnings.sup_uvp, f1->warnings.sup_ovp, f1->warnings.sup_ocp, f1->warnings.sup_opp); - printf("Out-OVP:%d Out-OCP:%d Out-OPP:%d Out-Short:%d Out-Open:%d \n", f1->warnings.out_ovp, f1->warnings.out_ocp, f1->warnings.out_opp, f1->warnings.out_short, f1->warnings.out_open); - printf("Sup-UVP:%d Sup-OVP:%d Sup-OCP:%d Sup-OPP:%d \n", f1->faults.sup_uvp, f1->faults.sup_ovp, f1->faults.sup_ocp, f1->faults.sup_opp); - printf("Out-OVP:%d Out-OCP:%d Out-OPP:%d Out-Short:%d Out-Open:%d \n", f1->faults.out_ovp, f1->faults.out_ocp, f1->faults.out_opp, f1->faults.out_short, f1->faults.out_open); -} - -static void print_ctrl_struct(hb_control_t* c1) -{ - printf("Enabled:%d \n", c1->enabled); - printf("Sup UVP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.sup_uvp.severity, c1->out_faults.sup_uvp.w_time, c1->out_faults.sup_uvp.f_time); - printf("Sup OVP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.sup_ovp.severity, c1->out_faults.sup_ovp.w_time, c1->out_faults.sup_ovp.f_time); - printf("Sup OCP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.sup_ocp.severity, c1->out_faults.sup_ocp.w_time, c1->out_faults.sup_ocp.f_time); - printf("Sup OPP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.sup_opp.severity, c1->out_faults.sup_opp.w_time, c1->out_faults.sup_opp.f_time); - printf("Out OVP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.out_ovp.severity, c1->out_faults.out_ovp.w_time, c1->out_faults.out_ovp.f_time); - printf("Out OCP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.out_ocp.severity, c1->out_faults.out_ocp.w_time, c1->out_faults.out_ocp.f_time); - printf("Out OPP: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.out_opp.severity, c1->out_faults.out_opp.w_time, c1->out_faults.out_opp.f_time); - printf("Out Short: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.out_short.severity, c1->out_faults.out_short.w_time, c1->out_faults.out_short.f_time); - printf("Out Open: Sev:%d W-time:%d F-time:%d \n", c1->out_faults.out_open.severity, c1->out_faults.out_open.w_time, c1->out_faults.out_open.f_time); - printf("EN-Sup-uvp:%d EN-Sup-ovp:%d EN-Sup-ocp:%d EN-Sup-opp:%d \n", c1->out_faults_en.sup_uvp, c1->out_faults_en.sup_ovp, c1->out_faults_en.sup_ocp, c1->out_faults_en.sup_opp); - printf("EN-Out-ovp:%d EN-Out-ocp:%d EN-Out-opp:%d EN-Out-open:%d EN-Out-short:%d \n", c1->out_faults_en.out_ovp, c1->out_faults_en.out_ocp, c1->out_faults_en.out_opp, c1->out_faults_en.out_open, c1->out_faults_en.out_short); - printf("Fuse: state:%d count:%d timer:%d \n", c1->out_fuse.state, c1->out_fuse.count, c1->out_fuse.timer); - printf("Fuse-Cfg: cooldown:%d Retry-time:%d \n", c1->out_fuse_cfg.cooldown_time, c1->out_fuse_cfg.retry_time); -} - -static int ut_hb_is_equal_fb_struct(hb_feedback_t* f1, hb_feedback_t* f2, uint8_t exp_out, hb_feedback_t* exp_f1, hb_feedback_t* exp_f2) -{ - uint8_t out = hb_is_equal_fb_struct(f1, f2); - - int f1_equal = 1; - if(f1->analog.out_voltage != exp_f1->analog.out_voltage ) f1_equal=0; - if(f1->analog.out_current != exp_f1->analog.out_current ) f1_equal= 0; - if(f1->analog.sup_voltage != exp_f1->analog.sup_voltage ) f1_equal= 0; - if(f1->analog.sup_current != exp_f1->analog.sup_current ) f1_equal= 0; - if(f1->analog.out_power != exp_f1->analog.out_power ) f1_equal= 0; - if(f1->analog.sup_power != exp_f1->analog.sup_power ) f1_equal= 0; - if(f1->analog.out_impedance != exp_f1->analog.out_impedance ) f1_equal= 0; - if(f1->analog.low_side_ctrl != exp_f1->analog.low_side_ctrl ) f1_equal= 0; - if(f1->analog.pwm != exp_f1->analog.pwm ) f1_equal= 0; - if(f1->enabled != exp_f1->enabled ) f1_equal= 0; - if(f1->warning_act != exp_f1->warning_act ) f1_equal= 0; - if(f1->fault_act != exp_f1->fault_act ) f1_equal= 0; - if(f1->fused != exp_f1->fused ) f1_equal= 0; - if(f1->warnings.sup_uvp != exp_f1->warnings.sup_uvp ) f1_equal= 0; - if(f1->warnings.sup_ovp != exp_f1->warnings.sup_ovp ) f1_equal= 0; - if(f1->warnings.sup_ocp != exp_f1->warnings.sup_ocp ) f1_equal= 0; - if(f1->warnings.sup_opp != exp_f1->warnings.sup_opp ) f1_equal= 0; - if(f1->warnings.out_ovp != exp_f1->warnings.out_ovp ) f1_equal= 0; - if(f1->warnings.out_ocp != exp_f1->warnings.out_ocp ) f1_equal= 0; - if(f1->warnings.out_opp != exp_f1->warnings.out_opp ) f1_equal= 0; - if(f1->warnings.out_short != exp_f1->warnings.out_short ) f1_equal= 0; - if(f1->warnings.out_open != exp_f1->warnings.out_open ) f1_equal= 0; - if(f1->faults.sup_uvp != exp_f1->faults.sup_uvp ) f1_equal= 0; - if(f1->faults.sup_ovp != exp_f1->faults.sup_ovp ) f1_equal= 0; - if(f1->faults.sup_ocp != exp_f1->faults.sup_ocp ) f1_equal= 0; - if(f1->faults.sup_opp != exp_f1->faults.sup_opp ) f1_equal= 0; - if(f1->faults.out_ovp != exp_f1->faults.out_ovp ) f1_equal= 0; - if(f1->faults.out_ocp != exp_f1->faults.out_ocp ) f1_equal= 0; - if(f1->faults.out_opp != exp_f1->faults.out_opp ) f1_equal= 0; - if(f1->faults.out_short != exp_f1->faults.out_short ) f1_equal= 0; - if(f1->faults.out_open != exp_f1->faults.out_open ) f1_equal= 0; - - int f2_equal = 1; - if(f2->analog.out_voltage != exp_f2->analog.out_voltage ) f2_equal=0; - if(f2->analog.out_current != exp_f2->analog.out_current ) f2_equal= 0; - if(f2->analog.sup_voltage != exp_f2->analog.sup_voltage ) f2_equal= 0; - if(f2->analog.sup_current != exp_f2->analog.sup_current ) f2_equal= 0; - if(f2->analog.out_power != exp_f2->analog.out_power ) f2_equal= 0; - if(f2->analog.sup_power != exp_f2->analog.sup_power ) f2_equal= 0; - if(f2->analog.out_impedance != exp_f2->analog.out_impedance ) f2_equal= 0; - if(f2->analog.low_side_ctrl != exp_f2->analog.low_side_ctrl ) f2_equal= 0; - if(f2->analog.pwm != exp_f2->analog.pwm ) f2_equal= 0; - if(f2->enabled != exp_f2->enabled ) f2_equal= 0; - if(f2->warning_act != exp_f2->warning_act ) f2_equal= 0; - if(f2->fault_act != exp_f2->fault_act ) f2_equal= 0; - if(f2->fused != exp_f2->fused ) f2_equal= 0; - if(f2->warnings.sup_uvp != exp_f2->warnings.sup_uvp ) f2_equal= 0; - if(f2->warnings.sup_ovp != exp_f2->warnings.sup_ovp ) f2_equal= 0; - if(f2->warnings.sup_ocp != exp_f2->warnings.sup_ocp ) f2_equal= 0; - if(f2->warnings.sup_opp != exp_f2->warnings.sup_opp ) f2_equal= 0; - if(f2->warnings.out_ovp != exp_f2->warnings.out_ovp ) f2_equal= 0; - if(f2->warnings.out_ocp != exp_f2->warnings.out_ocp ) f2_equal= 0; - if(f2->warnings.out_opp != exp_f2->warnings.out_opp ) f2_equal= 0; - if(f2->warnings.out_short != exp_f2->warnings.out_short ) f2_equal= 0; - if(f2->warnings.out_open != exp_f2->warnings.out_open ) f2_equal= 0; - if(f2->faults.sup_uvp != exp_f2->faults.sup_uvp ) f2_equal= 0; - if(f2->faults.sup_ovp != exp_f2->faults.sup_ovp ) f2_equal= 0; - if(f2->faults.sup_ocp != exp_f2->faults.sup_ocp ) f2_equal= 0; - if(f2->faults.sup_opp != exp_f2->faults.sup_opp ) f2_equal= 0; - if(f2->faults.out_ovp != exp_f2->faults.out_ovp ) f2_equal= 0; - if(f2->faults.out_ocp != exp_f2->faults.out_ocp ) f2_equal= 0; - if(f2->faults.out_opp != exp_f2->faults.out_opp ) f2_equal= 0; - if(f2->faults.out_short != exp_f2->faults.out_short ) f2_equal= 0; - if(f2->faults.out_open != exp_f2->faults.out_open ) f2_equal= 0; - - printf("f1-unchanged:%d f2-unchanged:%d \n", f1_equal, f2_equal); - printf(" Output: %d \n", out); - printf("Expected: %d \n", exp_out); - - if((out==exp_out)&&(f1_equal)&&(f2_equal)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hb_is_equal_fb_struct_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t hb_is_equal_fb_struct(hb_feedback_t* f1, hb_feedback_t* f2) \n"); - - int test_res; - int pass = 1; - - hb_feedback_t f1; - hb_feedback_t f2; - - uint8_t exp_out; - hb_feedback_t exp_f1; - hb_feedback_t exp_f2; - - // EQUAL - f1.analog.out_voltage = 0xAAAA; - f1.analog.out_current = 0xAAAA; - f1.analog.sup_voltage = 0xAAAA; - f1.analog.sup_current = 0xAAAA; - f1.analog.out_power = 0xAAAA; - f1.analog.sup_power = 0xAAAA; - f1.analog.out_impedance = 0xAAAA; - f1.analog.low_side_ctrl = 0xAA; - f1.analog.pwm = 0xAAAA; - f1.enabled = 0xAA; - f1.warning_act = 0xAA; - f1.fault_act = 0xAA; - f1.fused = 0xAA; - f1.warnings.sup_uvp = 0xAA; - f1.warnings.sup_ovp = 0xAA; - f1.warnings.sup_ocp = 0xAA; - f1.warnings.sup_opp = 0xAA; - f1.warnings.out_ovp = 0xAA; - f1.warnings.out_ocp = 0xAA; - f1.warnings.out_opp = 0xAA; - f1.warnings.out_short = 0xAA; - f1.warnings.out_open = 0xAA; - f1.faults.sup_uvp = 0xAA; - f1.faults.sup_ovp = 0xAA; - f1.faults.sup_ocp = 0xAA; - f1.faults.sup_opp = 0xAA; - f1.faults.out_ovp = 0xAA; - f1.faults.out_ocp = 0xAA; - f1.faults.out_opp = 0xAA; - f1.faults.out_short = 0xAA; - f1.faults.out_open = 0xAA; - - f2.analog.out_voltage = 0xAAAA; - f2.analog.out_current = 0xAAAA; - f2.analog.sup_voltage = 0xAAAA; - f2.analog.sup_current = 0xAAAA; - f2.analog.out_power = 0xAAAA; - f2.analog.sup_power = 0xAAAA; - f2.analog.out_impedance = 0xAAAA; - f2.analog.low_side_ctrl = 0xAA; - f2.analog.pwm = 0xAAAA; - f2.enabled = 0xAA; - f2.warning_act = 0xAA; - f2.fault_act = 0xAA; - f2.fused = 0xAA; - f2.warnings.sup_uvp = 0xAA; - f2.warnings.sup_ovp = 0xAA; - f2.warnings.sup_ocp = 0xAA; - f2.warnings.sup_opp = 0xAA; - f2.warnings.out_ovp = 0xAA; - f2.warnings.out_ocp = 0xAA; - f2.warnings.out_opp = 0xAA; - f2.warnings.out_short = 0xAA; - f2.warnings.out_open = 0xAA; - f2.faults.sup_uvp = 0xAA; - f2.faults.sup_ovp = 0xAA; - f2.faults.sup_ocp = 0xAA; - f2.faults.sup_opp = 0xAA; - f2.faults.out_ovp = 0xAA; - f2.faults.out_ocp = 0xAA; - f2.faults.out_opp = 0xAA; - f2.faults.out_short = 0xAA; - f2.faults.out_open = 0xAA; - - exp_out = 1; - - exp_f1.analog.out_voltage = f1.analog.out_voltage; - exp_f1.analog.out_current = f1.analog.out_current; - exp_f1.analog.sup_voltage = f1.analog.sup_voltage; - exp_f1.analog.sup_current = f1.analog.sup_current; - exp_f1.analog.out_power = f1.analog.out_power; - exp_f1.analog.sup_power = f1.analog.sup_power; - exp_f1.analog.out_impedance = f1.analog.out_impedance; - exp_f1.analog.low_side_ctrl = f1.analog.low_side_ctrl; - exp_f1.analog.pwm = f1.analog.pwm; - exp_f1.enabled = f1.enabled; - exp_f1.warning_act = f1.warning_act; - exp_f1.fault_act = f1.fault_act; - exp_f1.fused = f1.fused; - exp_f1.warnings.sup_uvp = f1.warnings.sup_uvp; - exp_f1.warnings.sup_ovp = f1.warnings.sup_ovp; - exp_f1.warnings.sup_ocp = f1.warnings.sup_ocp; - exp_f1.warnings.sup_opp = f1.warnings.sup_opp; - exp_f1.warnings.out_ovp = f1.warnings.out_ovp; - exp_f1.warnings.out_ocp = f1.warnings.out_ocp; - exp_f1.warnings.out_opp = f1.warnings.out_opp; - exp_f1.warnings.out_short = f1.warnings.out_short; - exp_f1.warnings.out_open = f1.warnings.out_open; - exp_f1.faults.sup_uvp = f1.faults.sup_uvp; - exp_f1.faults.sup_ovp = f1.faults.sup_ovp; - exp_f1.faults.sup_ocp = f1.faults.sup_ocp; - exp_f1.faults.sup_opp = f1.faults.sup_opp; - exp_f1.faults.out_ovp = f1.faults.out_ovp; - exp_f1.faults.out_ocp = f1.faults.out_ocp; - exp_f1.faults.out_opp = f1.faults.out_opp; - exp_f1.faults.out_short = f1.faults.out_short; - exp_f1.faults.out_open = f1.faults.out_open; - - exp_f2.analog.out_voltage = f2.analog.out_voltage; - exp_f2.analog.out_current = f2.analog.out_current; - exp_f2.analog.sup_voltage = f2.analog.sup_voltage; - exp_f2.analog.sup_current = f2.analog.sup_current; - exp_f2.analog.out_power = f2.analog.out_power; - exp_f2.analog.sup_power = f2.analog.sup_power; - exp_f2.analog.out_impedance = f2.analog.out_impedance; - exp_f2.analog.low_side_ctrl = f2.analog.low_side_ctrl; - exp_f2.analog.pwm = f2.analog.pwm; - exp_f2.enabled = f2.enabled; - exp_f2.warning_act = f2.warning_act; - exp_f2.fault_act = f2.fault_act; - exp_f2.fused = f2.fused; - exp_f2.warnings.sup_uvp = f2.warnings.sup_uvp; - exp_f2.warnings.sup_ovp = f2.warnings.sup_ovp; - exp_f2.warnings.sup_ocp = f2.warnings.sup_ocp; - exp_f2.warnings.sup_opp = f2.warnings.sup_opp; - exp_f2.warnings.out_ovp = f2.warnings.out_ovp; - exp_f2.warnings.out_ocp = f2.warnings.out_ocp; - exp_f2.warnings.out_opp = f2.warnings.out_opp; - exp_f2.warnings.out_short = f2.warnings.out_short; - exp_f2.warnings.out_open = f2.warnings.out_open; - exp_f2.faults.sup_uvp = f2.faults.sup_uvp; - exp_f2.faults.sup_ovp = f2.faults.sup_ovp; - exp_f2.faults.sup_ocp = f2.faults.sup_ocp; - exp_f2.faults.sup_opp = f2.faults.sup_opp; - exp_f2.faults.out_ovp = f2.faults.out_ovp; - exp_f2.faults.out_ocp = f2.faults.out_ocp; - exp_f2.faults.out_opp = f2.faults.out_opp; - exp_f2.faults.out_short = f2.faults.out_short; - exp_f2.faults.out_open = f2.faults.out_open; - - test_res = ut_hb_is_equal_fb_struct(&f1, &f2, exp_out, &exp_f1, &exp_f2); - if(!test_res) pass = 0; - - // NOT EQUAL - f1.analog.out_voltage = 0xAAAA; - f1.analog.out_current = 0xAAAA; - f1.analog.sup_voltage = 0xAAAA; - f1.analog.sup_current = 0xAAAA; - f1.analog.out_power = 0xAAAA; - f1.analog.sup_power = 0xAAAA; - f1.analog.out_impedance = 0xAAAA; - f1.analog.low_side_ctrl = 0xAA; - f1.analog.pwm = 0xAAAA; - f1.enabled = 0xAA; - f1.warning_act = 0xAA; - f1.fault_act = 0xAA; - f1.fused = 0xAA; - f1.warnings.sup_uvp = 0xAA; - f1.warnings.sup_ovp = 0xAA; - f1.warnings.sup_ocp = 0xAA; - f1.warnings.sup_opp = 0xAA; - f1.warnings.out_ovp = 0xAA; - f1.warnings.out_ocp = 0xAA; - f1.warnings.out_opp = 0xAA; - f1.warnings.out_short = 0xAA; - f1.warnings.out_open = 0xAA; - f1.faults.sup_uvp = 0xAA; - f1.faults.sup_ovp = 0xAA; - f1.faults.sup_ocp = 0xAA; - f1.faults.sup_opp = 0xAA; - f1.faults.out_ovp = 0xAA; - f1.faults.out_ocp = 0xAA; - f1.faults.out_opp = 0xAA; - f1.faults.out_short = 0xAA; - f1.faults.out_open = 0xAA; - - f2.analog.out_voltage = 0x5555; - f2.analog.out_current = 0x5555; - f2.analog.sup_voltage = 0x5555; - f2.analog.sup_current = 0x5555; - f2.analog.out_power = 0x5555; - f2.analog.sup_power = 0x5555; - f2.analog.out_impedance = 0x5555; - f2.analog.low_side_ctrl = 0x55; - f2.analog.pwm = 0x5555; - f2.enabled = 0x55; - f2.warning_act = 0x55; - f2.fault_act = 0x55; - f2.fused = 0x55; - f2.warnings.sup_uvp = 0x55; - f2.warnings.sup_ovp = 0x55; - f2.warnings.sup_ocp = 0x55; - f2.warnings.sup_opp = 0x55; - f2.warnings.out_ovp = 0x55; - f2.warnings.out_ocp = 0x55; - f2.warnings.out_opp = 0x55; - f2.warnings.out_short = 0x55; - f2.warnings.out_open = 0x55; - f2.faults.sup_uvp = 0x55; - f2.faults.sup_ovp = 0x55; - f2.faults.sup_ocp = 0x55; - f2.faults.sup_opp = 0x55; - f2.faults.out_ovp = 0x55; - f2.faults.out_ocp = 0x55; - f2.faults.out_opp = 0x55; - f2.faults.out_short = 0x55; - f2.faults.out_open = 0x55; - - exp_out = 0; - - exp_f1.analog.out_voltage = f1.analog.out_voltage; - exp_f1.analog.out_current = f1.analog.out_current; - exp_f1.analog.sup_voltage = f1.analog.sup_voltage; - exp_f1.analog.sup_current = f1.analog.sup_current; - exp_f1.analog.out_power = f1.analog.out_power; - exp_f1.analog.sup_power = f1.analog.sup_power; - exp_f1.analog.out_impedance = f1.analog.out_impedance; - exp_f1.analog.low_side_ctrl = f1.analog.low_side_ctrl; - exp_f1.analog.pwm = f1.analog.pwm; - exp_f1.enabled = f1.enabled; - exp_f1.warning_act = f1.warning_act; - exp_f1.fault_act = f1.fault_act; - exp_f1.fused = f1.fused; - exp_f1.warnings.sup_uvp = f1.warnings.sup_uvp; - exp_f1.warnings.sup_ovp = f1.warnings.sup_ovp; - exp_f1.warnings.sup_ocp = f1.warnings.sup_ocp; - exp_f1.warnings.sup_opp = f1.warnings.sup_opp; - exp_f1.warnings.out_ovp = f1.warnings.out_ovp; - exp_f1.warnings.out_ocp = f1.warnings.out_ocp; - exp_f1.warnings.out_opp = f1.warnings.out_opp; - exp_f1.warnings.out_short = f1.warnings.out_short; - exp_f1.warnings.out_open = f1.warnings.out_open; - exp_f1.faults.sup_uvp = f1.faults.sup_uvp; - exp_f1.faults.sup_ovp = f1.faults.sup_ovp; - exp_f1.faults.sup_ocp = f1.faults.sup_ocp; - exp_f1.faults.sup_opp = f1.faults.sup_opp; - exp_f1.faults.out_ovp = f1.faults.out_ovp; - exp_f1.faults.out_ocp = f1.faults.out_ocp; - exp_f1.faults.out_opp = f1.faults.out_opp; - exp_f1.faults.out_short = f1.faults.out_short; - exp_f1.faults.out_open = f1.faults.out_open; - - exp_f2.analog.out_voltage = f2.analog.out_voltage; - exp_f2.analog.out_current = f2.analog.out_current; - exp_f2.analog.sup_voltage = f2.analog.sup_voltage; - exp_f2.analog.sup_current = f2.analog.sup_current; - exp_f2.analog.out_power = f2.analog.out_power; - exp_f2.analog.sup_power = f2.analog.sup_power; - exp_f2.analog.out_impedance = f2.analog.out_impedance; - exp_f2.analog.low_side_ctrl = f2.analog.low_side_ctrl; - exp_f2.analog.pwm = f2.analog.pwm; - exp_f2.enabled = f2.enabled; - exp_f2.warning_act = f2.warning_act; - exp_f2.fault_act = f2.fault_act; - exp_f2.fused = f2.fused; - exp_f2.warnings.sup_uvp = f2.warnings.sup_uvp; - exp_f2.warnings.sup_ovp = f2.warnings.sup_ovp; - exp_f2.warnings.sup_ocp = f2.warnings.sup_ocp; - exp_f2.warnings.sup_opp = f2.warnings.sup_opp; - exp_f2.warnings.out_ovp = f2.warnings.out_ovp; - exp_f2.warnings.out_ocp = f2.warnings.out_ocp; - exp_f2.warnings.out_opp = f2.warnings.out_opp; - exp_f2.warnings.out_short = f2.warnings.out_short; - exp_f2.warnings.out_open = f2.warnings.out_open; - exp_f2.faults.sup_uvp = f2.faults.sup_uvp; - exp_f2.faults.sup_ovp = f2.faults.sup_ovp; - exp_f2.faults.sup_ocp = f2.faults.sup_ocp; - exp_f2.faults.sup_opp = f2.faults.sup_opp; - exp_f2.faults.out_ovp = f2.faults.out_ovp; - exp_f2.faults.out_ocp = f2.faults.out_ocp; - exp_f2.faults.out_opp = f2.faults.out_opp; - exp_f2.faults.out_short = f2.faults.out_short; - exp_f2.faults.out_open = f2.faults.out_open; - - test_res = ut_hb_is_equal_fb_struct(&f1, &f2, exp_out, &exp_f1, &exp_f2); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_hb_is_equal_ctrl_struct(hb_control_t* c1, hb_control_t* c2, uint8_t exp_out, hb_control_t* exp_c1, hb_control_t* exp_c2) -{ - uint8_t out = hb_is_equal_ctrl_struct(c1, c2); - - int c1_equal = 1; - if(c1->enabled != exp_c1->enabled ) c1_equal = 0; - if(c1->out_faults.sup_uvp.severity != exp_c1->out_faults.sup_uvp.severity ) c1_equal = 0; - if(c1->out_faults.sup_uvp.w_time != exp_c1->out_faults.sup_uvp.w_time ) c1_equal = 0; - if(c1->out_faults.sup_uvp.f_time != exp_c1->out_faults.sup_uvp.f_time ) c1_equal = 0; - if(c1->out_faults.sup_ovp.severity != exp_c1->out_faults.sup_ovp.severity ) c1_equal = 0; - if(c1->out_faults.sup_ovp.w_time != exp_c1->out_faults.sup_ovp.w_time ) c1_equal = 0; - if(c1->out_faults.sup_ovp.f_time != exp_c1->out_faults.sup_ovp.f_time ) c1_equal = 0; - if(c1->out_faults.sup_ocp.severity != exp_c1->out_faults.sup_ocp.severity ) c1_equal = 0; - if(c1->out_faults.sup_ocp.w_time != exp_c1->out_faults.sup_ocp.w_time ) c1_equal = 0; - if(c1->out_faults.sup_ocp.f_time != exp_c1->out_faults.sup_ocp.f_time ) c1_equal = 0; - if(c1->out_faults.sup_opp.severity != exp_c1->out_faults.sup_opp.severity ) c1_equal = 0; - if(c1->out_faults.sup_opp.w_time != exp_c1->out_faults.sup_opp.w_time ) c1_equal = 0; - if(c1->out_faults.sup_opp.f_time != exp_c1->out_faults.sup_opp.f_time ) c1_equal = 0; - if(c1->out_faults.out_ovp.severity != exp_c1->out_faults.out_ovp.severity ) c1_equal = 0; - if(c1->out_faults.out_ovp.w_time != exp_c1->out_faults.out_ovp.w_time ) c1_equal = 0; - if(c1->out_faults.out_ovp.f_time != exp_c1->out_faults.out_ovp.f_time ) c1_equal = 0; - if(c1->out_faults.out_ocp.severity != exp_c1->out_faults.out_ocp.severity ) c1_equal = 0; - if(c1->out_faults.out_ocp.w_time != exp_c1->out_faults.out_ocp.w_time ) c1_equal = 0; - if(c1->out_faults.out_ocp.f_time != exp_c1->out_faults.out_ocp.f_time ) c1_equal = 0; - if(c1->out_faults.out_opp.severity != exp_c1->out_faults.out_opp.severity ) c1_equal = 0; - if(c1->out_faults.out_opp.w_time != exp_c1->out_faults.out_opp.w_time ) c1_equal = 0; - if(c1->out_faults.out_opp.f_time != exp_c1->out_faults.out_opp.f_time ) c1_equal = 0; - if(c1->out_faults.out_short.severity != exp_c1->out_faults.out_short.severity ) c1_equal = 0; - if(c1->out_faults.out_short.w_time != exp_c1->out_faults.out_short.w_time ) c1_equal = 0; - if(c1->out_faults.out_short.f_time != exp_c1->out_faults.out_short.f_time ) c1_equal = 0; - if(c1->out_faults.out_open.severity != exp_c1->out_faults.out_open.severity ) c1_equal = 0; - if(c1->out_faults.out_open.w_time != exp_c1->out_faults.out_open.w_time ) c1_equal = 0; - if(c1->out_faults.out_open.f_time != exp_c1->out_faults.out_open.f_time ) c1_equal = 0; - if(c1->out_faults_en.sup_uvp != exp_c1->out_faults_en.sup_uvp ) c1_equal = 0; - if(c1->out_faults_en.sup_ovp != exp_c1->out_faults_en.sup_ovp ) c1_equal = 0; - if(c1->out_faults_en.sup_ocp != exp_c1->out_faults_en.sup_ocp ) c1_equal = 0; - if(c1->out_faults_en.sup_opp != exp_c1->out_faults_en.sup_opp ) c1_equal = 0; - if(c1->out_faults_en.out_ovp != exp_c1->out_faults_en.out_ovp ) c1_equal = 0; - if(c1->out_faults_en.out_ocp != exp_c1->out_faults_en.out_ocp ) c1_equal = 0; - if(c1->out_faults_en.out_opp != exp_c1->out_faults_en.out_opp ) c1_equal = 0; - if(c1->out_faults_en.out_short != exp_c1->out_faults_en.out_short ) c1_equal = 0; - if(c1->out_faults_en.out_open != exp_c1->out_faults_en.out_open ) c1_equal = 0; - if(c1->out_fuse.state != exp_c1->out_fuse.state ) c1_equal = 0; - if(c1->out_fuse.count != exp_c1->out_fuse.count ) c1_equal = 0; - if(c1->out_fuse.timer != exp_c1->out_fuse.timer ) c1_equal = 0; - if(c1->out_fuse_cfg.cooldown_time != exp_c1->out_fuse_cfg.cooldown_time ) c1_equal = 0; - if(c1->out_fuse_cfg.retry_time != exp_c1->out_fuse_cfg.retry_time ) c1_equal = 0; - - int c2_equal = 1; - if(c2->enabled != exp_c2->enabled ) c2_equal = 0; - if(c2->out_faults.sup_uvp.severity != exp_c2->out_faults.sup_uvp.severity ) c2_equal = 0; - if(c2->out_faults.sup_uvp.w_time != exp_c2->out_faults.sup_uvp.w_time ) c2_equal = 0; - if(c2->out_faults.sup_uvp.f_time != exp_c2->out_faults.sup_uvp.f_time ) c2_equal = 0; - if(c2->out_faults.sup_ovp.severity != exp_c2->out_faults.sup_ovp.severity ) c2_equal = 0; - if(c2->out_faults.sup_ovp.w_time != exp_c2->out_faults.sup_ovp.w_time ) c2_equal = 0; - if(c2->out_faults.sup_ovp.f_time != exp_c2->out_faults.sup_ovp.f_time ) c2_equal = 0; - if(c2->out_faults.sup_ocp.severity != exp_c2->out_faults.sup_ocp.severity ) c2_equal = 0; - if(c2->out_faults.sup_ocp.w_time != exp_c2->out_faults.sup_ocp.w_time ) c2_equal = 0; - if(c2->out_faults.sup_ocp.f_time != exp_c2->out_faults.sup_ocp.f_time ) c2_equal = 0; - if(c2->out_faults.sup_opp.severity != exp_c2->out_faults.sup_opp.severity ) c2_equal = 0; - if(c2->out_faults.sup_opp.w_time != exp_c2->out_faults.sup_opp.w_time ) c2_equal = 0; - if(c2->out_faults.sup_opp.f_time != exp_c2->out_faults.sup_opp.f_time ) c2_equal = 0; - if(c2->out_faults.out_ovp.severity != exp_c2->out_faults.out_ovp.severity ) c2_equal = 0; - if(c2->out_faults.out_ovp.w_time != exp_c2->out_faults.out_ovp.w_time ) c2_equal = 0; - if(c2->out_faults.out_ovp.f_time != exp_c2->out_faults.out_ovp.f_time ) c2_equal = 0; - if(c2->out_faults.out_ocp.severity != exp_c2->out_faults.out_ocp.severity ) c2_equal = 0; - if(c2->out_faults.out_ocp.w_time != exp_c2->out_faults.out_ocp.w_time ) c2_equal = 0; - if(c2->out_faults.out_ocp.f_time != exp_c2->out_faults.out_ocp.f_time ) c2_equal = 0; - if(c2->out_faults.out_opp.severity != exp_c2->out_faults.out_opp.severity ) c2_equal = 0; - if(c2->out_faults.out_opp.w_time != exp_c2->out_faults.out_opp.w_time ) c2_equal = 0; - if(c2->out_faults.out_opp.f_time != exp_c2->out_faults.out_opp.f_time ) c2_equal = 0; - if(c2->out_faults.out_short.severity != exp_c2->out_faults.out_short.severity ) c2_equal = 0; - if(c2->out_faults.out_short.w_time != exp_c2->out_faults.out_short.w_time ) c2_equal = 0; - if(c2->out_faults.out_short.f_time != exp_c2->out_faults.out_short.f_time ) c2_equal = 0; - if(c2->out_faults.out_open.severity != exp_c2->out_faults.out_open.severity ) c2_equal = 0; - if(c2->out_faults.out_open.w_time != exp_c2->out_faults.out_open.w_time ) c2_equal = 0; - if(c2->out_faults.out_open.f_time != exp_c2->out_faults.out_open.f_time ) c2_equal = 0; - if(c2->out_faults_en.sup_uvp != exp_c2->out_faults_en.sup_uvp ) c2_equal = 0; - if(c2->out_faults_en.sup_ovp != exp_c2->out_faults_en.sup_ovp ) c2_equal = 0; - if(c2->out_faults_en.sup_ocp != exp_c2->out_faults_en.sup_ocp ) c2_equal = 0; - if(c2->out_faults_en.sup_opp != exp_c2->out_faults_en.sup_opp ) c2_equal = 0; - if(c2->out_faults_en.out_ovp != exp_c2->out_faults_en.out_ovp ) c2_equal = 0; - if(c2->out_faults_en.out_ocp != exp_c2->out_faults_en.out_ocp ) c2_equal = 0; - if(c2->out_faults_en.out_opp != exp_c2->out_faults_en.out_opp ) c2_equal = 0; - if(c2->out_faults_en.out_short != exp_c2->out_faults_en.out_short ) c2_equal = 0; - if(c2->out_faults_en.out_open != exp_c2->out_faults_en.out_open ) c2_equal = 0; - if(c2->out_fuse.state != exp_c2->out_fuse.state ) c2_equal = 0; - if(c2->out_fuse.count != exp_c2->out_fuse.count ) c2_equal = 0; - if(c2->out_fuse.timer != exp_c2->out_fuse.timer ) c2_equal = 0; - if(c2->out_fuse_cfg.cooldown_time != exp_c2->out_fuse_cfg.cooldown_time ) c2_equal = 0; - if(c2->out_fuse_cfg.retry_time != exp_c2->out_fuse_cfg.retry_time ) c2_equal = 0; - - printf("c1-unchanged:%d c2-unchanged:%d \n", c1_equal, c2_equal); - printf(" Output: %d \n", out); - printf("Expected: %d \n", exp_out); - - if((out==exp_out)&&(c1_equal)&&(c2_equal)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hb_is_equal_ctrl_struct_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t hb_is_equal_ctrl_struct(hb_control_t* c1, hb_control_t* c2) \n"); - - int test_res; - int pass = 1; - - hb_control_t c1; - hb_control_t c2; - - uint8_t exp_out; - hb_control_t exp_c1; - hb_control_t exp_c2; - - // EQUAL - c1.enabled = 0xAA; - c1.out_faults.sup_uvp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_uvp.w_time = 0xAAAA; - c1.out_faults.sup_uvp.f_time = 0xAAAA; - c1.out_faults.sup_ovp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_ovp.w_time = 0xAAAA; - c1.out_faults.sup_ovp.f_time = 0xAAAA; - c1.out_faults.sup_ocp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_ocp.w_time = 0xAAAA; - c1.out_faults.sup_ocp.f_time = 0xAAAA; - c1.out_faults.sup_opp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_opp.w_time = 0xAAAA; - c1.out_faults.sup_opp.f_time = 0xAAAA; - c1.out_faults.out_ovp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_ovp.w_time = 0xAAAA; - c1.out_faults.out_ovp.f_time = 0xAAAA; - c1.out_faults.out_ocp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_ocp.w_time = 0xAAAA; - c1.out_faults.out_ocp.f_time = 0xAAAA; - c1.out_faults.out_opp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_opp.w_time = 0xAAAA; - c1.out_faults.out_opp.f_time = 0xAAAA; - c1.out_faults.out_short.severity = FAULT_LVL_FAULT; - c1.out_faults.out_short.w_time = 0xAAAA; - c1.out_faults.out_short.f_time = 0xAAAA; - c1.out_faults.out_open.severity = FAULT_LVL_FAULT; - c1.out_faults.out_open.w_time = 0xAAAA; - c1.out_faults.out_open.f_time = 0xAAAA; - c1.out_faults_en.sup_uvp = 0xAA; - c1.out_faults_en.sup_ovp = 0xAA; - c1.out_faults_en.sup_ocp = 0xAA; - c1.out_faults_en.sup_opp = 0xAA; - c1.out_faults_en.out_ovp = 0xAA; - c1.out_faults_en.out_ocp = 0xAA; - c1.out_faults_en.out_opp = 0xAA; - c1.out_faults_en.out_open = 0xAA; - c1.out_faults_en.out_short = 0xAA; - c1.out_fuse.state = FUSE_ACTIVE; - c1.out_fuse.count = 0xAA; - c1.out_fuse.timer = 0xAAAA; - c1.out_fuse_cfg.cooldown_time = 0xAAAA; - c1.out_fuse_cfg.retry_time = 0xAAAA; - - c2.enabled = 0xAA; - c2.out_faults.sup_uvp.severity = FAULT_LVL_FAULT; - c2.out_faults.sup_uvp.w_time = 0xAAAA; - c2.out_faults.sup_uvp.f_time = 0xAAAA; - c2.out_faults.sup_ovp.severity = FAULT_LVL_FAULT; - c2.out_faults.sup_ovp.w_time = 0xAAAA; - c2.out_faults.sup_ovp.f_time = 0xAAAA; - c2.out_faults.sup_ocp.severity = FAULT_LVL_FAULT; - c2.out_faults.sup_ocp.w_time = 0xAAAA; - c2.out_faults.sup_ocp.f_time = 0xAAAA; - c2.out_faults.sup_opp.severity = FAULT_LVL_FAULT; - c2.out_faults.sup_opp.w_time = 0xAAAA; - c2.out_faults.sup_opp.f_time = 0xAAAA; - c2.out_faults.out_ovp.severity = FAULT_LVL_FAULT; - c2.out_faults.out_ovp.w_time = 0xAAAA; - c2.out_faults.out_ovp.f_time = 0xAAAA; - c2.out_faults.out_ocp.severity = FAULT_LVL_FAULT; - c2.out_faults.out_ocp.w_time = 0xAAAA; - c2.out_faults.out_ocp.f_time = 0xAAAA; - c2.out_faults.out_opp.severity = FAULT_LVL_FAULT; - c2.out_faults.out_opp.w_time = 0xAAAA; - c2.out_faults.out_opp.f_time = 0xAAAA; - c2.out_faults.out_short.severity = FAULT_LVL_FAULT; - c2.out_faults.out_short.w_time = 0xAAAA; - c2.out_faults.out_short.f_time = 0xAAAA; - c2.out_faults.out_open.severity = FAULT_LVL_FAULT; - c2.out_faults.out_open.w_time = 0xAAAA; - c2.out_faults.out_open.f_time = 0xAAAA; - c2.out_faults_en.sup_uvp = 0xAA; - c2.out_faults_en.sup_ovp = 0xAA; - c2.out_faults_en.sup_ocp = 0xAA; - c2.out_faults_en.sup_opp = 0xAA; - c2.out_faults_en.out_ovp = 0xAA; - c2.out_faults_en.out_ocp = 0xAA; - c2.out_faults_en.out_opp = 0xAA; - c2.out_faults_en.out_open = 0xAA; - c2.out_faults_en.out_short = 0xAA; - c2.out_fuse.state = FUSE_ACTIVE; - c2.out_fuse.count = 0xAA; - c2.out_fuse.timer = 0xAAAA; - c2.out_fuse_cfg.cooldown_time = 0xAAAA; - c2.out_fuse_cfg.retry_time = 0xAAAA; - - exp_out = 1; - - exp_c1.enabled = c1.enabled; - exp_c1.out_faults.sup_uvp.severity = c1.out_faults.sup_uvp.severity; - exp_c1.out_faults.sup_uvp.w_time = c1.out_faults.sup_uvp.w_time; - exp_c1.out_faults.sup_uvp.f_time = c1.out_faults.sup_uvp.f_time; - exp_c1.out_faults.sup_ovp.severity = c1.out_faults.sup_ovp.severity; - exp_c1.out_faults.sup_ovp.w_time = c1.out_faults.sup_ovp.w_time; - exp_c1.out_faults.sup_ovp.f_time = c1.out_faults.sup_ovp.f_time; - exp_c1.out_faults.sup_ocp.severity = c1.out_faults.sup_ocp.severity; - exp_c1.out_faults.sup_ocp.w_time = c1.out_faults.sup_ocp.w_time; - exp_c1.out_faults.sup_ocp.f_time = c1.out_faults.sup_ocp.f_time; - exp_c1.out_faults.sup_opp.severity = c1.out_faults.sup_opp.severity; - exp_c1.out_faults.sup_opp.w_time = c1.out_faults.sup_opp.w_time; - exp_c1.out_faults.sup_opp.f_time = c1.out_faults.sup_opp.f_time; - exp_c1.out_faults.out_ovp.severity = c1.out_faults.out_ovp.severity; - exp_c1.out_faults.out_ovp.w_time = c1.out_faults.out_ovp.w_time; - exp_c1.out_faults.out_ovp.f_time = c1.out_faults.out_ovp.f_time; - exp_c1.out_faults.out_ocp.severity = c1.out_faults.out_ocp.severity; - exp_c1.out_faults.out_ocp.w_time = c1.out_faults.out_ocp.w_time; - exp_c1.out_faults.out_ocp.f_time = c1.out_faults.out_ocp.f_time; - exp_c1.out_faults.out_opp.severity = c1.out_faults.out_opp.severity; - exp_c1.out_faults.out_opp.w_time = c1.out_faults.out_opp.w_time; - exp_c1.out_faults.out_opp.f_time = c1.out_faults.out_opp.f_time; - exp_c1.out_faults.out_short.severity = c1.out_faults.out_short.severity; - exp_c1.out_faults.out_short.w_time = c1.out_faults.out_short.w_time; - exp_c1.out_faults.out_short.f_time = c1.out_faults.out_short.f_time; - exp_c1.out_faults.out_open.severity = c1.out_faults.out_open.severity; - exp_c1.out_faults.out_open.w_time = c1.out_faults.out_open.w_time; - exp_c1.out_faults.out_open.f_time = c1.out_faults.out_open.f_time; - exp_c1.out_faults_en.sup_uvp = c1.out_faults_en.sup_uvp; - exp_c1.out_faults_en.sup_ovp = c1.out_faults_en.sup_ovp; - exp_c1.out_faults_en.sup_ocp = c1.out_faults_en.sup_ocp; - exp_c1.out_faults_en.sup_opp = c1.out_faults_en.sup_opp; - exp_c1.out_faults_en.out_ovp = c1.out_faults_en.out_ovp; - exp_c1.out_faults_en.out_ocp = c1.out_faults_en.out_ocp; - exp_c1.out_faults_en.out_opp = c1.out_faults_en.out_opp; - exp_c1.out_faults_en.out_open = c1.out_faults_en.out_open; - exp_c1.out_faults_en.out_short = c1.out_faults_en.out_short; - exp_c1.out_fuse.state = c1.out_fuse.state; - exp_c1.out_fuse.count = c1.out_fuse.count; - exp_c1.out_fuse.timer = c1.out_fuse.timer; - exp_c1.out_fuse_cfg.cooldown_time = c1.out_fuse_cfg.cooldown_time; - exp_c1.out_fuse_cfg.retry_time = c1.out_fuse_cfg.retry_time; - - exp_c2.enabled = c2.enabled; - exp_c2.out_faults.sup_uvp.severity = c2.out_faults.sup_uvp.severity; - exp_c2.out_faults.sup_uvp.w_time = c2.out_faults.sup_uvp.w_time; - exp_c2.out_faults.sup_uvp.f_time = c2.out_faults.sup_uvp.f_time; - exp_c2.out_faults.sup_ovp.severity = c2.out_faults.sup_ovp.severity; - exp_c2.out_faults.sup_ovp.w_time = c2.out_faults.sup_ovp.w_time; - exp_c2.out_faults.sup_ovp.f_time = c2.out_faults.sup_ovp.f_time; - exp_c2.out_faults.sup_ocp.severity = c2.out_faults.sup_ocp.severity; - exp_c2.out_faults.sup_ocp.w_time = c2.out_faults.sup_ocp.w_time; - exp_c2.out_faults.sup_ocp.f_time = c2.out_faults.sup_ocp.f_time; - exp_c2.out_faults.sup_opp.severity = c2.out_faults.sup_opp.severity; - exp_c2.out_faults.sup_opp.w_time = c2.out_faults.sup_opp.w_time; - exp_c2.out_faults.sup_opp.f_time = c2.out_faults.sup_opp.f_time; - exp_c2.out_faults.out_ovp.severity = c2.out_faults.out_ovp.severity; - exp_c2.out_faults.out_ovp.w_time = c2.out_faults.out_ovp.w_time; - exp_c2.out_faults.out_ovp.f_time = c2.out_faults.out_ovp.f_time; - exp_c2.out_faults.out_ocp.severity = c2.out_faults.out_ocp.severity; - exp_c2.out_faults.out_ocp.w_time = c2.out_faults.out_ocp.w_time; - exp_c2.out_faults.out_ocp.f_time = c2.out_faults.out_ocp.f_time; - exp_c2.out_faults.out_opp.severity = c2.out_faults.out_opp.severity; - exp_c2.out_faults.out_opp.w_time = c2.out_faults.out_opp.w_time; - exp_c2.out_faults.out_opp.f_time = c2.out_faults.out_opp.f_time; - exp_c2.out_faults.out_short.severity = c2.out_faults.out_short.severity; - exp_c2.out_faults.out_short.w_time = c2.out_faults.out_short.w_time; - exp_c2.out_faults.out_short.f_time = c2.out_faults.out_short.f_time; - exp_c2.out_faults.out_open.severity = c2.out_faults.out_open.severity; - exp_c2.out_faults.out_open.w_time = c2.out_faults.out_open.w_time; - exp_c2.out_faults.out_open.f_time = c2.out_faults.out_open.f_time; - exp_c2.out_faults_en.sup_uvp = c2.out_faults_en.sup_uvp; - exp_c2.out_faults_en.sup_ovp = c2.out_faults_en.sup_ovp; - exp_c2.out_faults_en.sup_ocp = c2.out_faults_en.sup_ocp; - exp_c2.out_faults_en.sup_opp = c2.out_faults_en.sup_opp; - exp_c2.out_faults_en.out_ovp = c2.out_faults_en.out_ovp; - exp_c2.out_faults_en.out_ocp = c2.out_faults_en.out_ocp; - exp_c2.out_faults_en.out_opp = c2.out_faults_en.out_opp; - exp_c2.out_faults_en.out_open = c2.out_faults_en.out_open; - exp_c2.out_faults_en.out_short = c2.out_faults_en.out_short; - exp_c2.out_fuse.state = c2.out_fuse.state; - exp_c2.out_fuse.count = c2.out_fuse.count; - exp_c2.out_fuse.timer = c2.out_fuse.timer; - exp_c2.out_fuse_cfg.cooldown_time = c2.out_fuse_cfg.cooldown_time; - exp_c2.out_fuse_cfg.retry_time = c2.out_fuse_cfg.retry_time; - - test_res = ut_hb_is_equal_ctrl_struct(&c1, &c2, exp_out, &exp_c1, &exp_c2); - if(!test_res) pass = 0; - - // NOT EQUAL - c1.enabled = 0xAA; - c1.out_faults.sup_uvp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_uvp.w_time = 0xAAAA; - c1.out_faults.sup_uvp.f_time = 0xAAAA; - c1.out_faults.sup_ovp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_ovp.w_time = 0xAAAA; - c1.out_faults.sup_ovp.f_time = 0xAAAA; - c1.out_faults.sup_ocp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_ocp.w_time = 0xAAAA; - c1.out_faults.sup_ocp.f_time = 0xAAAA; - c1.out_faults.sup_opp.severity = FAULT_LVL_FAULT; - c1.out_faults.sup_opp.w_time = 0xAAAA; - c1.out_faults.sup_opp.f_time = 0xAAAA; - c1.out_faults.out_ovp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_ovp.w_time = 0xAAAA; - c1.out_faults.out_ovp.f_time = 0xAAAA; - c1.out_faults.out_ocp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_ocp.w_time = 0xAAAA; - c1.out_faults.out_ocp.f_time = 0xAAAA; - c1.out_faults.out_opp.severity = FAULT_LVL_FAULT; - c1.out_faults.out_opp.w_time = 0xAAAA; - c1.out_faults.out_opp.f_time = 0xAAAA; - c1.out_faults.out_short.severity = FAULT_LVL_FAULT; - c1.out_faults.out_short.w_time = 0xAAAA; - c1.out_faults.out_short.f_time = 0xAAAA; - c1.out_faults.out_open.severity = FAULT_LVL_FAULT; - c1.out_faults.out_open.w_time = 0xAAAA; - c1.out_faults.out_open.f_time = 0xAAAA; - c1.out_faults_en.sup_uvp = 0xAA; - c1.out_faults_en.sup_ovp = 0xAA; - c1.out_faults_en.sup_ocp = 0xAA; - c1.out_faults_en.sup_opp = 0xAA; - c1.out_faults_en.out_ovp = 0xAA; - c1.out_faults_en.out_ocp = 0xAA; - c1.out_faults_en.out_opp = 0xAA; - c1.out_faults_en.out_open = 0xAA; - c1.out_faults_en.out_short = 0xAA; - c1.out_fuse.state = FUSE_ACTIVE; - c1.out_fuse.count = 0xAA; - c1.out_fuse.timer = 0xAAAA; - c1.out_fuse_cfg.cooldown_time = 0xAAAA; - c1.out_fuse_cfg.retry_time = 0xAAAA; - - c2.enabled = 0x55; - c2.out_faults.sup_uvp.severity = FAULT_LVL_WARNING; - c2.out_faults.sup_uvp.w_time = 0x5555; - c2.out_faults.sup_uvp.f_time = 0x5555; - c2.out_faults.sup_ovp.severity = FAULT_LVL_WARNING; - c2.out_faults.sup_ovp.w_time = 0x5555; - c2.out_faults.sup_ovp.f_time = 0x5555; - c2.out_faults.sup_ocp.severity = FAULT_LVL_WARNING; - c2.out_faults.sup_ocp.w_time = 0x5555; - c2.out_faults.sup_ocp.f_time = 0x5555; - c2.out_faults.sup_opp.severity = FAULT_LVL_WARNING; - c2.out_faults.sup_opp.w_time = 0x5555; - c2.out_faults.sup_opp.f_time = 0x5555; - c2.out_faults.out_ovp.severity = FAULT_LVL_WARNING; - c2.out_faults.out_ovp.w_time = 0x5555; - c2.out_faults.out_ovp.f_time = 0x5555; - c2.out_faults.out_ocp.severity = FAULT_LVL_WARNING; - c2.out_faults.out_ocp.w_time = 0x5555; - c2.out_faults.out_ocp.f_time = 0x5555; - c2.out_faults.out_opp.severity = FAULT_LVL_WARNING; - c2.out_faults.out_opp.w_time = 0x5555; - c2.out_faults.out_opp.f_time = 0x5555; - c2.out_faults.out_short.severity = FAULT_LVL_WARNING; - c2.out_faults.out_short.w_time = 0x5555; - c2.out_faults.out_short.f_time = 0x5555; - c2.out_faults.out_open.severity = FAULT_LVL_WARNING; - c2.out_faults.out_open.w_time = 0x5555; - c2.out_faults.out_open.f_time = 0x5555; - c2.out_faults_en.sup_uvp = 0x55; - c2.out_faults_en.sup_ovp = 0x55; - c2.out_faults_en.sup_ocp = 0x55; - c2.out_faults_en.sup_opp = 0x55; - c2.out_faults_en.out_ovp = 0x55; - c2.out_faults_en.out_ocp = 0x55; - c2.out_faults_en.out_opp = 0x55; - c2.out_faults_en.out_open = 0x55; - c2.out_faults_en.out_short = 0x55; - c2.out_fuse.state = FUSE_COOLDOWN; - c2.out_fuse.count = 0x55; - c2.out_fuse.timer = 0x5555; - c2.out_fuse_cfg.cooldown_time = 0x5555; - c2.out_fuse_cfg.retry_time = 0x5555; - - exp_out = 0; - - exp_c1.enabled = c1.enabled; - exp_c1.out_faults.sup_uvp.severity = c1.out_faults.sup_uvp.severity; - exp_c1.out_faults.sup_uvp.w_time = c1.out_faults.sup_uvp.w_time; - exp_c1.out_faults.sup_uvp.f_time = c1.out_faults.sup_uvp.f_time; - exp_c1.out_faults.sup_ovp.severity = c1.out_faults.sup_ovp.severity; - exp_c1.out_faults.sup_ovp.w_time = c1.out_faults.sup_ovp.w_time; - exp_c1.out_faults.sup_ovp.f_time = c1.out_faults.sup_ovp.f_time; - exp_c1.out_faults.sup_ocp.severity = c1.out_faults.sup_ocp.severity; - exp_c1.out_faults.sup_ocp.w_time = c1.out_faults.sup_ocp.w_time; - exp_c1.out_faults.sup_ocp.f_time = c1.out_faults.sup_ocp.f_time; - exp_c1.out_faults.sup_opp.severity = c1.out_faults.sup_opp.severity; - exp_c1.out_faults.sup_opp.w_time = c1.out_faults.sup_opp.w_time; - exp_c1.out_faults.sup_opp.f_time = c1.out_faults.sup_opp.f_time; - exp_c1.out_faults.out_ovp.severity = c1.out_faults.out_ovp.severity; - exp_c1.out_faults.out_ovp.w_time = c1.out_faults.out_ovp.w_time; - exp_c1.out_faults.out_ovp.f_time = c1.out_faults.out_ovp.f_time; - exp_c1.out_faults.out_ocp.severity = c1.out_faults.out_ocp.severity; - exp_c1.out_faults.out_ocp.w_time = c1.out_faults.out_ocp.w_time; - exp_c1.out_faults.out_ocp.f_time = c1.out_faults.out_ocp.f_time; - exp_c1.out_faults.out_opp.severity = c1.out_faults.out_opp.severity; - exp_c1.out_faults.out_opp.w_time = c1.out_faults.out_opp.w_time; - exp_c1.out_faults.out_opp.f_time = c1.out_faults.out_opp.f_time; - exp_c1.out_faults.out_short.severity = c1.out_faults.out_short.severity; - exp_c1.out_faults.out_short.w_time = c1.out_faults.out_short.w_time; - exp_c1.out_faults.out_short.f_time = c1.out_faults.out_short.f_time; - exp_c1.out_faults.out_open.severity = c1.out_faults.out_open.severity; - exp_c1.out_faults.out_open.w_time = c1.out_faults.out_open.w_time; - exp_c1.out_faults.out_open.f_time = c1.out_faults.out_open.f_time; - exp_c1.out_faults_en.sup_uvp = c1.out_faults_en.sup_uvp; - exp_c1.out_faults_en.sup_ovp = c1.out_faults_en.sup_ovp; - exp_c1.out_faults_en.sup_ocp = c1.out_faults_en.sup_ocp; - exp_c1.out_faults_en.sup_opp = c1.out_faults_en.sup_opp; - exp_c1.out_faults_en.out_ovp = c1.out_faults_en.out_ovp; - exp_c1.out_faults_en.out_ocp = c1.out_faults_en.out_ocp; - exp_c1.out_faults_en.out_opp = c1.out_faults_en.out_opp; - exp_c1.out_faults_en.out_open = c1.out_faults_en.out_open; - exp_c1.out_faults_en.out_short = c1.out_faults_en.out_short; - exp_c1.out_fuse.state = c1.out_fuse.state; - exp_c1.out_fuse.count = c1.out_fuse.count; - exp_c1.out_fuse.timer = c1.out_fuse.timer; - exp_c1.out_fuse_cfg.cooldown_time = c1.out_fuse_cfg.cooldown_time; - exp_c1.out_fuse_cfg.retry_time = c1.out_fuse_cfg.retry_time; - - exp_c2.enabled = c2.enabled; - exp_c2.out_faults.sup_uvp.severity = c2.out_faults.sup_uvp.severity; - exp_c2.out_faults.sup_uvp.w_time = c2.out_faults.sup_uvp.w_time; - exp_c2.out_faults.sup_uvp.f_time = c2.out_faults.sup_uvp.f_time; - exp_c2.out_faults.sup_ovp.severity = c2.out_faults.sup_ovp.severity; - exp_c2.out_faults.sup_ovp.w_time = c2.out_faults.sup_ovp.w_time; - exp_c2.out_faults.sup_ovp.f_time = c2.out_faults.sup_ovp.f_time; - exp_c2.out_faults.sup_ocp.severity = c2.out_faults.sup_ocp.severity; - exp_c2.out_faults.sup_ocp.w_time = c2.out_faults.sup_ocp.w_time; - exp_c2.out_faults.sup_ocp.f_time = c2.out_faults.sup_ocp.f_time; - exp_c2.out_faults.sup_opp.severity = c2.out_faults.sup_opp.severity; - exp_c2.out_faults.sup_opp.w_time = c2.out_faults.sup_opp.w_time; - exp_c2.out_faults.sup_opp.f_time = c2.out_faults.sup_opp.f_time; - exp_c2.out_faults.out_ovp.severity = c2.out_faults.out_ovp.severity; - exp_c2.out_faults.out_ovp.w_time = c2.out_faults.out_ovp.w_time; - exp_c2.out_faults.out_ovp.f_time = c2.out_faults.out_ovp.f_time; - exp_c2.out_faults.out_ocp.severity = c2.out_faults.out_ocp.severity; - exp_c2.out_faults.out_ocp.w_time = c2.out_faults.out_ocp.w_time; - exp_c2.out_faults.out_ocp.f_time = c2.out_faults.out_ocp.f_time; - exp_c2.out_faults.out_opp.severity = c2.out_faults.out_opp.severity; - exp_c2.out_faults.out_opp.w_time = c2.out_faults.out_opp.w_time; - exp_c2.out_faults.out_opp.f_time = c2.out_faults.out_opp.f_time; - exp_c2.out_faults.out_short.severity = c2.out_faults.out_short.severity; - exp_c2.out_faults.out_short.w_time = c2.out_faults.out_short.w_time; - exp_c2.out_faults.out_short.f_time = c2.out_faults.out_short.f_time; - exp_c2.out_faults.out_open.severity = c2.out_faults.out_open.severity; - exp_c2.out_faults.out_open.w_time = c2.out_faults.out_open.w_time; - exp_c2.out_faults.out_open.f_time = c2.out_faults.out_open.f_time; - exp_c2.out_faults_en.sup_uvp = c2.out_faults_en.sup_uvp; - exp_c2.out_faults_en.sup_ovp = c2.out_faults_en.sup_ovp; - exp_c2.out_faults_en.sup_ocp = c2.out_faults_en.sup_ocp; - exp_c2.out_faults_en.sup_opp = c2.out_faults_en.sup_opp; - exp_c2.out_faults_en.out_ovp = c2.out_faults_en.out_ovp; - exp_c2.out_faults_en.out_ocp = c2.out_faults_en.out_ocp; - exp_c2.out_faults_en.out_opp = c2.out_faults_en.out_opp; - exp_c2.out_faults_en.out_open = c2.out_faults_en.out_open; - exp_c2.out_faults_en.out_short = c2.out_faults_en.out_short; - exp_c2.out_fuse.state = c2.out_fuse.state; - exp_c2.out_fuse.count = c2.out_fuse.count; - exp_c2.out_fuse.timer = c2.out_fuse.timer; - exp_c2.out_fuse_cfg.cooldown_time = c2.out_fuse_cfg.cooldown_time; - exp_c2.out_fuse_cfg.retry_time = c2.out_fuse_cfg.retry_time; - - test_res = ut_hb_is_equal_ctrl_struct(&c1, &c2, exp_out, &exp_c1, &exp_c2); - if(!test_res) pass = 0; - - return pass; -} - - -static const uint8_t NOT_ACCESD_LOW = 255; -static const uint16_t NOT_ACCESD_PWM = 0xFFFF; - -static int ut_hb_init(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl, hb_meas_t* meas, uint8_t exp_low, uint16_t exp_pwm, hb_feedback_t* exp_hb_fb, hb_control_t* exp_hb_ctrl) -{ - printf("Input:\n"); - - mock_board_halfbridge_write_low(NOT_ACCESD_LOW); - mock_board_halfbridge_write_pwm(NOT_ACCESD_PWM); - mock_board_halfbridge_write_feedback(meas); - - hb_init(hb_fb, hb_ctrl); - - uint8_t out_low = mock_board_halfbridge_read_low(); - uint16_t out_pwm = mock_board_halfbridge_read_pwm(); - uint8_t fb_ok = hb_is_equal_fb_struct(hb_fb, exp_hb_fb); - uint8_t ctrl_ok = hb_is_equal_ctrl_struct(hb_ctrl, exp_hb_ctrl); - - printf(" Output: Low:%d PWM:%d FB:%d CTRL:%d \n", out_low, out_pwm, fb_ok, ctrl_ok); - printf("Expected: Low:%d PWM:%d \n", exp_low, exp_pwm); - - if((out_low==exp_low)&&(out_pwm==exp_pwm)&&(fb_ok)&&(ctrl_ok)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hb_init_test(void) -{ - printf("******************************************************\n"); - printf("void hb_init(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl) \n"); - - int test_res; - int pass = 1; - - hb_feedback_t hb_fb; - hb_control_t hb_ctrl; - hb_meas_t meas; - uint8_t exp_low; - uint16_t exp_pwm; - hb_feedback_t exp_hb_fb; - hb_control_t exp_hb_ctrl; - - // Normal 1 - hb_fb.analog.out_voltage = 0xAAAA; - hb_fb.analog.out_current = 0xAAAA; - hb_fb.analog.sup_voltage = 0xAAAA; - hb_fb.analog.sup_current = 0xAAAA; - hb_fb.analog.out_power = 0xAAAA; - hb_fb.analog.sup_power = 0xAAAA; - hb_fb.analog.out_impedance = 0xAAAA; - hb_fb.analog.low_side_ctrl = 0xAA; - hb_fb.analog.pwm = 0xAAAA; - hb_fb.enabled = 0xAA; - hb_fb.warning_act = 0xAA; - hb_fb.fault_act = 0xAA; - hb_fb.fused = 0xAA; - hb_fb.warnings.sup_uvp = 0xAA; - hb_fb.warnings.sup_ovp = 0xAA; - hb_fb.warnings.sup_ocp = 0xAA; - hb_fb.warnings.sup_opp = 0xAA; - hb_fb.warnings.out_ovp = 0xAA; - hb_fb.warnings.out_ocp = 0xAA; - hb_fb.warnings.out_opp = 0xAA; - hb_fb.warnings.out_short = 0xAA; - hb_fb.warnings.out_open = 0xAA; - hb_fb.faults.sup_uvp = 0xAA; - hb_fb.faults.sup_ovp = 0xAA; - hb_fb.faults.sup_ocp = 0xAA; - hb_fb.faults.sup_opp = 0xAA; - hb_fb.faults.out_ovp = 0xAA; - hb_fb.faults.out_ocp = 0xAA; - hb_fb.faults.out_opp = 0xAA; - hb_fb.faults.out_short = 0xAA; - hb_fb.faults.out_open = 0xAA; - - hb_ctrl.enabled = 0xAA; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.sup_uvp.w_time = 0xAAAA; - hb_ctrl.out_faults.sup_uvp.f_time = 0xAAAA; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.sup_ovp.w_time = 0xAAAA; - hb_ctrl.out_faults.sup_ovp.f_time = 0xAAAA; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.sup_ocp.w_time = 0xAAAA; - hb_ctrl.out_faults.sup_ocp.f_time = 0xAAAA; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.sup_opp.w_time = 0xAAAA; - hb_ctrl.out_faults.sup_opp.f_time = 0xAAAA; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.out_ovp.w_time = 0xAAAA; - hb_ctrl.out_faults.out_ovp.f_time = 0xAAAA; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.out_ocp.w_time = 0xAAAA; - hb_ctrl.out_faults.out_ocp.f_time = 0xAAAA; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.out_opp.w_time = 0xAAAA; - hb_ctrl.out_faults.out_opp.f_time = 0xAAAA; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.out_short.w_time = 0xAAAA; - hb_ctrl.out_faults.out_short.f_time = 0xAAAA; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_FAULT; - hb_ctrl.out_faults.out_open.w_time = 0xAAAA; - hb_ctrl.out_faults.out_open.f_time = 0xAAAA; - hb_ctrl.out_faults_en.sup_uvp = 0xAA; - hb_ctrl.out_faults_en.sup_ovp = 0xAA; - hb_ctrl.out_faults_en.sup_ocp = 0xAA; - hb_ctrl.out_faults_en.sup_opp = 0xAA; - hb_ctrl.out_faults_en.out_ovp = 0xAA; - hb_ctrl.out_faults_en.out_ocp = 0xAA; - hb_ctrl.out_faults_en.out_opp = 0xAA; - hb_ctrl.out_faults_en.out_short = 0xAA; - hb_ctrl.out_faults_en.out_open = 0xAA; - hb_ctrl.out_fuse.state = FUSE_ACTIVE; - hb_ctrl.out_fuse.count = 0xAAAA; - hb_ctrl.out_fuse.timer = 0xAAAA; - hb_ctrl.out_fuse_cfg.cooldown_time = 0xAAAA; - hb_ctrl.out_fuse_cfg.retry_time = 0xAAAA; - - meas.out_voltage = 12000; - meas.out_current = 100; - meas.sup_voltage = 0; - meas.sup_current = 0; - meas.out_power = 0; - meas.sup_power = 1200; - meas.out_impedance = 0xFFFF; - meas.low_side_ctrl = 0; - meas.pwm = 0; - - exp_low = 0; - exp_pwm = 0; - - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 0; - exp_hb_fb.warning_act = 0; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 0; - exp_hb_fb.warnings.sup_opp = 0; - exp_hb_fb.warnings.out_ovp = 0; - exp_hb_fb.warnings.out_ocp = 0; - exp_hb_fb.warnings.out_opp = 0; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 0; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 0; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_init(&hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_hb_enable(hb_control_t* hb_ctrl, uint8_t exp_low, uint16_t exp_pwm, hb_control_t* exp_hb_ctrl) -{ - printf(" Input:\n"); - - mock_board_halfbridge_write_low(NOT_ACCESD_LOW); - mock_board_halfbridge_write_pwm(NOT_ACCESD_PWM); - - hb_enable(hb_ctrl); - - uint8_t out_low = mock_board_halfbridge_read_low(); - uint16_t out_pwm = mock_board_halfbridge_read_pwm(); - uint8_t ctrl_ok = hb_is_equal_ctrl_struct(hb_ctrl, exp_hb_ctrl); - - printf(" Output: Low:%d PWM:%d Ctrl:%d\n", out_low, out_pwm, ctrl_ok); - printf("Expected: Low:%d PWM:%d \n", exp_low, exp_pwm); - - if((out_low==exp_low)&&(out_pwm==exp_pwm)&&(ctrl_ok)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hb_enable_test(void) -{ - printf("******************************************************\n"); - printf("void hb_enable(hb_control_t* hb_ctrl) \n"); - - int test_res; - int pass = 1; - - hb_control_t hb_ctrl; - uint8_t exp_low; - uint16_t exp_pwm; - hb_control_t exp_hb_ctrl; - - // Normal 1 - hb_ctrl.enabled = 0; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 0; - hb_ctrl.out_faults_en.sup_ovp = 0; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 0; - hb_ctrl.out_faults_en.out_ovp = 0; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 0; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 0; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - exp_low = 1; - exp_pwm = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 0; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_enable(&hb_ctrl, exp_low, exp_pwm, &exp_hb_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_hb_disable(hb_control_t* hb_ctrl, uint8_t exp_low, uint16_t exp_pwm, hb_control_t* exp_hb_ctrl) -{ - printf(" Input:\n"); - - mock_board_halfbridge_write_low(NOT_ACCESD_LOW); - mock_board_halfbridge_write_pwm(NOT_ACCESD_PWM); - - hb_disable(hb_ctrl); - - uint8_t out_low = mock_board_halfbridge_read_low(); - uint16_t out_pwm = mock_board_halfbridge_read_pwm(); - uint8_t ctrl_ok = hb_is_equal_ctrl_struct(hb_ctrl, exp_hb_ctrl); - - printf(" Output: Low:%d PWM:%d Ctrl:%d\n", out_low, out_pwm, ctrl_ok); - printf("Expected: Low:%d PWM:%d \n", exp_low, exp_pwm); - - if((out_low==exp_low)&&(out_pwm==exp_pwm)&&(ctrl_ok)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - - -int ut_hb_disable_test(void) -{ - printf("******************************************************\n"); - printf("void hb_disable(hb_control_t* hb_ctrl) \n"); - - int test_res; - int pass = 1; - - hb_control_t hb_ctrl; - uint8_t exp_low; - uint16_t exp_pwm; - hb_control_t exp_hb_ctrl; - - // Normal 1 - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 0; - hb_ctrl.out_faults_en.sup_ovp = 0; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 0; - hb_ctrl.out_faults_en.out_ovp = 0; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 0; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 0; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - exp_low = 0; - exp_pwm = 0; - - exp_hb_ctrl.enabled = 0; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 0; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_disable(&hb_ctrl, exp_low, exp_pwm, &exp_hb_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_hb_process(int16_t target, hb_feedback_t* hb_fb, hb_control_t* hb_ctrl, hb_meas_t* meas, uint8_t exp_low, uint16_t exp_pwm, hb_feedback_t* exp_hb_fb, hb_control_t* exp_hb_ctrl) -{ - printf(" Input: Target:%d Enabled:%d \n", target, hb_ctrl->enabled); - printf("Supply Voltage:%d Current:%d Power:%d \n", meas->sup_voltage, meas->sup_current, meas->sup_power); - printf("Output Voltage:%d Current:%d Power:%d Impedance:%d\n", meas->out_voltage, meas->out_current, meas->out_power, meas->out_impedance); - printf("Low side:%d PWM:%d \n", meas->low_side_ctrl, meas->pwm); - printf("Control struct input: \n"); - print_ctrl_struct(hb_ctrl); - - mock_board_halfbridge_write_low(NOT_ACCESD_LOW); - mock_board_halfbridge_write_pwm(NOT_ACCESD_PWM); - mock_board_halfbridge_write_feedback(meas); - - hb_process(target, hb_fb, hb_ctrl); - - uint8_t out_low = mock_board_halfbridge_read_low(); - uint16_t out_pwm = mock_board_halfbridge_read_pwm(); - uint8_t fb_ok = hb_is_equal_fb_struct(hb_fb, exp_hb_fb); - uint8_t ctrl_ok = hb_is_equal_ctrl_struct(hb_ctrl, exp_hb_ctrl); - - printf("\n"); - printf("Output: \n"); - printf("Low:%d PWM:%d FB:%d CTRL:%d \n", out_low, out_pwm, fb_ok, ctrl_ok); - print_fb_struct(hb_fb); - print_ctrl_struct(hb_ctrl); - - printf("\n"); - printf("Expected: \n"); - printf("Low:%d PWM:%d \n", exp_low, exp_pwm); - print_ctrl_struct(exp_hb_ctrl); - - if((out_low==exp_low)&&(out_pwm==exp_pwm)&&(fb_ok)&&(ctrl_ok)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hb_process_test(void) -{ - printf("******************************************************\n"); - printf("void hb_process(int16_t target, hb_feedback_t* hb_fb, hb_control_t* hb_ctrl) \n"); - - int test_res; - int pass = 1; - - int16_t target; - hb_feedback_t hb_fb; - hb_control_t hb_ctrl; - hb_meas_t meas; - - uint8_t exp_low; - uint16_t exp_pwm; - hb_feedback_t exp_hb_fb; - hb_control_t exp_hb_ctrl; - - // 0V target - printf("----- 0V target ------------------ \n"); - target = 0; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 0; - hb_ctrl.out_faults_en.sup_ovp = 0; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 0; - hb_ctrl.out_faults_en.out_ovp = 0; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 0; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 0; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = 12000; - meas.sup_current = 100; - meas.sup_power = 1200; - meas.out_voltage = 0; - meas.out_current = 0; - meas.out_power = 0; - meas.out_impedance = 0xFFFF; - meas.low_side_ctrl = 1; - meas.pwm = 0; - - exp_low = 1; - exp_pwm = 0; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 0; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 0; - exp_hb_fb.warnings.sup_opp = 0; - exp_hb_fb.warnings.out_ovp = 0; - exp_hb_fb.warnings.out_ocp = 0; - exp_hb_fb.warnings.out_opp = 0; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 0; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // 6V target - printf("----- 6V target ------------------ \n"); - target = 6000; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 0; - hb_ctrl.out_faults_en.sup_ovp = 0; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 0; - hb_ctrl.out_faults_en.out_ovp = 1; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 0; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 0; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = 12000; - meas.sup_current = 100; - meas.sup_power = 1200; - meas.out_voltage = 0; - meas.out_current = 0; - meas.out_power = 0; - meas.out_impedance = 0xFFFF; - meas.low_side_ctrl = 1; - meas.pwm = 0; - - exp_low = 1; - exp_pwm = 0x7FFF; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 0; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 0; - exp_hb_fb.warnings.sup_opp = 0; - exp_hb_fb.warnings.out_ovp = 0; - exp_hb_fb.warnings.out_ocp = 0; - exp_hb_fb.warnings.out_opp = 0; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // -1 / Handbrake target - printf("----- -1 Handbarke target ------------------ \n"); - target = -1; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 0; - hb_ctrl.out_faults_en.sup_ovp = 0; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 0; - hb_ctrl.out_faults_en.out_ovp = 0; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 0; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 0; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = 12000; - meas.sup_current = 100; - meas.sup_power = 1200; - meas.out_voltage = 0; - meas.out_current = 0; - meas.out_power = 0; - meas.out_impedance = 0xFFFF; - meas.low_side_ctrl = 1; - meas.pwm = 0; - - exp_low = 0; - exp_pwm = 0; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 0; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 0; - exp_hb_fb.warnings.sup_opp = 0; - exp_hb_fb.warnings.out_ovp = 0; - exp_hb_fb.warnings.out_ocp = 0; - exp_hb_fb.warnings.out_opp = 0; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 0; - exp_hb_ctrl.out_faults_en.sup_ovp = 0; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 0; - exp_hb_ctrl.out_faults_en.out_ovp = 0; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 0; - exp_hb_ctrl.out_faults_en.out_short = 0; - exp_hb_ctrl.out_faults_en.out_open = 0; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // Warnings 1 - printf("----- Warning 1 ------------------ \n"); - target = 1000; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 1; - hb_ctrl.out_faults_en.sup_ovp = 1; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 1; - hb_ctrl.out_faults_en.out_ovp = 1; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 1; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 1; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = HW_HB_SUPPLY_VOLT_MAX_W+1000; - meas.sup_current = HW_HB_SUPPLY_CURRENT_MAX_W+1000; - meas.sup_power = HW_HB_SUPPLY_POWER_MAX_W+1000; - meas.out_voltage = HW_HB_OUT_VOLTAGE_MAX_W+1000; - meas.out_current = HW_HB_OUT_CURRENT_MAX_W+1000; - meas.out_power = HW_HB_OUT_POWER_MAX_W+1000; - meas.out_impedance = HW_HB_OUT_RESISTANCE_MIN_W-50; - meas.low_side_ctrl = 1; - meas.pwm = 0xFF00; - - exp_low = 1; - exp_pwm = 3855; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 1; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 1; - exp_hb_fb.warnings.sup_ocp = 1; - exp_hb_fb.warnings.sup_opp = 1; - exp_hb_fb.warnings.out_ovp = 1; - exp_hb_fb.warnings.out_ocp = 1; - exp_hb_fb.warnings.out_opp = 1; - exp_hb_fb.warnings.out_short = 1; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 1; - exp_hb_ctrl.out_faults_en.sup_ovp = 1; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 1; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 1; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 1; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // Warnings 2 - printf("----- Warning 2 ------------------ \n"); - target = 5000; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 1; - hb_ctrl.out_faults_en.sup_ovp = 1; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 1; - hb_ctrl.out_faults_en.out_ovp = 1; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 1; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 1; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = HW_HB_SUPPLY_VOLT_MIN_W-1000; - meas.sup_current = HW_HB_SUPPLY_CURRENT_MAX_W+1000; - meas.sup_power = HW_HB_SUPPLY_POWER_MAX_W+1000; - meas.out_voltage = HW_HB_OUT_VOLTAGE_MAX_W+1000; - meas.out_current = HW_HB_OUT_CURRENT_MAX_W+1000; - meas.out_power = HW_HB_OUT_POWER_MAX_W+1000; - meas.out_impedance = HW_HB_OUT_RESISTANCE_MAX_W+50; - meas.low_side_ctrl = 1; - meas.pwm = 0xFF00; - - exp_low = 1; - exp_pwm = 36408; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 1; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 1; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 1; - exp_hb_fb.warnings.sup_opp = 1; - exp_hb_fb.warnings.out_ovp = 1; - exp_hb_fb.warnings.out_ocp = 1; - exp_hb_fb.warnings.out_opp = 1; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 1; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 1; - exp_hb_ctrl.out_faults_en.sup_ovp = 1; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 1; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 1; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 1; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // Faults 1 - printf("----- Faults 1 first sample ------------------ \n"); - target = 2500; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_uvp.w_time = 0; - hb_ctrl.out_faults.sup_uvp.f_time = 0; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ovp.w_time = 0; - hb_ctrl.out_faults.sup_ovp.f_time = 0; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_ocp.w_time = 0; - hb_ctrl.out_faults.sup_ocp.f_time = 0; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.sup_opp.w_time = 0; - hb_ctrl.out_faults.sup_opp.f_time = 0; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ovp.w_time = 0; - hb_ctrl.out_faults.out_ovp.f_time = 0; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_ocp.w_time = 0; - hb_ctrl.out_faults.out_ocp.f_time = 0; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_opp.w_time = 0; - hb_ctrl.out_faults.out_opp.f_time = 0; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_short.w_time = 0; - hb_ctrl.out_faults.out_short.f_time = 0; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - hb_ctrl.out_faults.out_open.w_time = 0; - hb_ctrl.out_faults.out_open.f_time = 0; - hb_ctrl.out_faults_en.sup_uvp = 1; - hb_ctrl.out_faults_en.sup_ovp = 1; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 1; - hb_ctrl.out_faults_en.out_ovp = 1; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 1; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 1; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = 20000; - meas.sup_current = 10000; - meas.sup_power = 50000; - meas.out_voltage = 12000; - meas.out_current = 10000; - meas.out_power = 50000; - meas.out_impedance = 200; - meas.low_side_ctrl = 1; - meas.pwm = 0xFF00; - - exp_low = 1; - exp_pwm = 8191; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 1; - exp_hb_fb.fault_act = 0; - exp_hb_fb.fused = 0; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 1; - exp_hb_fb.warnings.sup_ocp = 1; - exp_hb_fb.warnings.sup_opp = 1; - exp_hb_fb.warnings.out_ovp = 1; - exp_hb_fb.warnings.out_ocp = 1; - exp_hb_fb.warnings.out_opp = 1; - exp_hb_fb.warnings.out_short = 1; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 0; - exp_hb_fb.faults.sup_ocp = 0; - exp_hb_fb.faults.sup_opp = 0; - exp_hb_fb.faults.out_ovp = 0; - exp_hb_fb.faults.out_ocp = 0; - exp_hb_fb.faults.out_opp = 0; - exp_hb_fb.faults.out_short = 0; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 0; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 0; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.sup_opp.w_time = 0; - exp_hb_ctrl.out_faults.sup_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ovp.w_time = 0; - exp_hb_ctrl.out_faults.out_ovp.f_time = 0; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_ocp.w_time = 0; - exp_hb_ctrl.out_faults.out_ocp.f_time = 0; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_opp.w_time = 0; - exp_hb_ctrl.out_faults.out_opp.f_time = 0; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_WARNING; - exp_hb_ctrl.out_faults.out_short.w_time = 0; - exp_hb_ctrl.out_faults.out_short.f_time = 0; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 1; - exp_hb_ctrl.out_faults_en.sup_ovp = 1; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 1; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 1; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 1; - exp_hb_ctrl.out_fuse.state = FUSE_OFF; - exp_hb_ctrl.out_fuse.count = 0; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - // Faults 1 - printf("----- Faults 1 transition sample ------------------ \n"); - target = 2500; - - hb_fb.analog.sup_voltage = 12000; - hb_fb.analog.sup_current = 100; - hb_fb.analog.sup_power = 1200; - hb_fb.analog.out_voltage = 0; - hb_fb.analog.out_current = 0; - hb_fb.analog.out_power = 0; - hb_fb.analog.out_impedance = 0xFFFF; - hb_fb.analog.low_side_ctrl = 1; - hb_fb.analog.pwm = 0; - hb_fb.enabled = 1; - hb_fb.warning_act = 0; - hb_fb.fault_act = 0; - hb_fb.fused = 0; - hb_fb.warnings.sup_uvp = 0; - hb_fb.warnings.sup_ovp = 0; - hb_fb.warnings.sup_ocp = 0; - hb_fb.warnings.sup_opp = 0; - hb_fb.warnings.out_ovp = 0; - hb_fb.warnings.out_ocp = 0; - hb_fb.warnings.out_opp = 0; - hb_fb.warnings.out_short = 0; - hb_fb.warnings.out_open = 0; - hb_fb.faults.sup_uvp = 0; - hb_fb.faults.sup_ovp = 0; - hb_fb.faults.sup_ocp = 0; - hb_fb.faults.sup_opp = 0; - hb_fb.faults.out_ovp = 0; - hb_fb.faults.out_ocp = 0; - hb_fb.faults.out_opp = 0; - hb_fb.faults.out_short = 0; - hb_fb.faults.out_open = 0; - - hb_ctrl.enabled = 1; - hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.sup_uvp.w_time = 499; - hb_ctrl.out_faults.sup_uvp.f_time = 499; - hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.sup_ovp.w_time = 499; - hb_ctrl.out_faults.sup_ovp.f_time = 499; - hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.sup_ocp.w_time = 499; - hb_ctrl.out_faults.sup_ocp.f_time = 499; - hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.sup_opp.w_time = 499; - hb_ctrl.out_faults.sup_opp.f_time = 499; - hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.out_ovp.w_time = 499; - hb_ctrl.out_faults.out_ovp.f_time = 499; - hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.out_ocp.w_time = 499; - hb_ctrl.out_faults.out_ocp.f_time = 499; - hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.out_opp.w_time = 499; - hb_ctrl.out_faults.out_opp.f_time = 499; - hb_ctrl.out_faults.out_short.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.out_short.w_time = 499; - hb_ctrl.out_faults.out_short.f_time = 499; - hb_ctrl.out_faults.out_open.severity = FAULT_LVL_WARNING; - hb_ctrl.out_faults.out_open.w_time = 499; - hb_ctrl.out_faults.out_open.f_time = 499; - hb_ctrl.out_faults_en.sup_uvp = 1; - hb_ctrl.out_faults_en.sup_ovp = 1; - hb_ctrl.out_faults_en.sup_ocp = 1; - hb_ctrl.out_faults_en.sup_opp = 1; - hb_ctrl.out_faults_en.out_ovp = 1; - hb_ctrl.out_faults_en.out_ocp = 1; - hb_ctrl.out_faults_en.out_opp = 1; - hb_ctrl.out_faults_en.out_short = 1; - hb_ctrl.out_faults_en.out_open = 1; - hb_ctrl.out_fuse.state = FUSE_OFF; - hb_ctrl.out_fuse.count = 0; - hb_ctrl.out_fuse.timer = 0; - hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - hb_ctrl.out_fuse_cfg.retry_time = 1000; - - meas.sup_voltage = 20000; - meas.sup_current = 10000; - meas.sup_power = 50000; - meas.out_voltage = 12000; - meas.out_current = 10000; - meas.out_power = 50000; - meas.out_impedance = 200; - meas.low_side_ctrl = 1; - meas.pwm = 0xFF00; - - exp_low = 0; - exp_pwm = 0; - - exp_hb_fb.analog.sup_voltage = meas.sup_voltage; - exp_hb_fb.analog.sup_current = meas.sup_current; - exp_hb_fb.analog.sup_power = meas.sup_power; - exp_hb_fb.analog.out_voltage = meas.out_voltage; - exp_hb_fb.analog.out_current = meas.out_current; - exp_hb_fb.analog.out_power = meas.out_power; - exp_hb_fb.analog.out_impedance = meas.out_impedance; - exp_hb_fb.analog.low_side_ctrl = meas.low_side_ctrl; - exp_hb_fb.analog.pwm = meas.pwm; - exp_hb_fb.enabled = 1; - exp_hb_fb.warning_act = 0; - exp_hb_fb.fault_act = 1; - exp_hb_fb.fused = 1; - exp_hb_fb.warnings.sup_uvp = 0; - exp_hb_fb.warnings.sup_ovp = 0; - exp_hb_fb.warnings.sup_ocp = 0; - exp_hb_fb.warnings.sup_opp = 0; - exp_hb_fb.warnings.out_ovp = 0; - exp_hb_fb.warnings.out_ocp = 0; - exp_hb_fb.warnings.out_opp = 0; - exp_hb_fb.warnings.out_short = 0; - exp_hb_fb.warnings.out_open = 0; - exp_hb_fb.faults.sup_uvp = 0; - exp_hb_fb.faults.sup_ovp = 1; - exp_hb_fb.faults.sup_ocp = 1; - exp_hb_fb.faults.sup_opp = 1; - exp_hb_fb.faults.out_ovp = 1; - exp_hb_fb.faults.out_ocp = 1; - exp_hb_fb.faults.out_opp = 1; - exp_hb_fb.faults.out_short = 1; - exp_hb_fb.faults.out_open = 0; - - exp_hb_ctrl.enabled = 1; - exp_hb_ctrl.out_faults.sup_uvp.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.sup_uvp.w_time = 0; - exp_hb_ctrl.out_faults.sup_uvp.f_time = 0; - exp_hb_ctrl.out_faults.sup_ovp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.sup_ovp.w_time = 500; - exp_hb_ctrl.out_faults.sup_ovp.f_time = 500; - exp_hb_ctrl.out_faults.sup_ocp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.sup_ocp.w_time = 500; - exp_hb_ctrl.out_faults.sup_ocp.f_time = 500; - exp_hb_ctrl.out_faults.sup_opp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.sup_opp.w_time = 500; - exp_hb_ctrl.out_faults.sup_opp.f_time = 500; - exp_hb_ctrl.out_faults.out_ovp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.out_ovp.w_time = 500; - exp_hb_ctrl.out_faults.out_ovp.f_time = 500; - exp_hb_ctrl.out_faults.out_ocp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.out_ocp.w_time = 500; - exp_hb_ctrl.out_faults.out_ocp.f_time = 500; - exp_hb_ctrl.out_faults.out_opp.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.out_opp.w_time = 500; - exp_hb_ctrl.out_faults.out_opp.f_time = 500; - exp_hb_ctrl.out_faults.out_short.severity = FAULT_LVL_FAULT; - exp_hb_ctrl.out_faults.out_short.w_time = 500; - exp_hb_ctrl.out_faults.out_short.f_time = 500; - exp_hb_ctrl.out_faults.out_open.severity = FAULT_LVL_OK; - exp_hb_ctrl.out_faults.out_open.w_time = 0; - exp_hb_ctrl.out_faults.out_open.f_time = 0; - exp_hb_ctrl.out_faults_en.sup_uvp = 1; - exp_hb_ctrl.out_faults_en.sup_ovp = 1; - exp_hb_ctrl.out_faults_en.sup_ocp = 1; - exp_hb_ctrl.out_faults_en.sup_opp = 1; - exp_hb_ctrl.out_faults_en.out_ovp = 1; - exp_hb_ctrl.out_faults_en.out_ocp = 1; - exp_hb_ctrl.out_faults_en.out_opp = 1; - exp_hb_ctrl.out_faults_en.out_short = 1; - exp_hb_ctrl.out_faults_en.out_open = 1; - exp_hb_ctrl.out_fuse.state = FUSE_ACTIVE; - exp_hb_ctrl.out_fuse.count = 1; - exp_hb_ctrl.out_fuse.timer = 0; - exp_hb_ctrl.out_fuse_cfg.cooldown_time = 1000; - exp_hb_ctrl.out_fuse_cfg.retry_time = 1000; - - test_res = ut_hb_process(target, &hb_fb, &hb_ctrl, &meas, exp_low, exp_pwm, &exp_hb_fb, &exp_hb_ctrl); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_hw/ut_hb_control.h b/firmware/tests/ut_hw/ut_hb_control.h deleted file mode 100644 index 5dbc2d4..0000000 --- a/firmware/tests/ut_hw/ut_hb_control.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef UT_HW_HB_CONTROL_H_ -#define UT_HW_HB_CONTROL_H_ - -#include -#include - -int ut_hb_is_equal_fb_struct_test(void); -int ut_hb_is_equal_ctrl_struct_test(void); - -int ut_hb_init_test(void); -int ut_hb_enable_test(void); -int ut_hb_disable_test(void); -int ut_hb_process_test(void); - -#endif /* UT_HW_HB_CONTROL_H_ */ diff --git a/firmware/tests/ut_hw/ut_led_display.c b/firmware/tests/ut_hw/ut_led_display.c deleted file mode 100644 index cce012d..0000000 --- a/firmware/tests/ut_hw/ut_led_display.c +++ /dev/null @@ -1,170 +0,0 @@ -#include "ut_led_display.h" - -#include "..\mock_board\mock_board_odout.h" - -#include "..\..\src\hw\led_display.h" - -static uint8_t NOT_ACCESD_OD_CHANNEL = 255; -static uint8_t NOT_ACCESD_OD_LEVEL = BSP_ODOUT_HIGH; -static uint8_t NOT_ACCESD_PERCENT = 255; - -static int ut_led_dsp_set_image(uint8_t image, uint8_t exp_image) -{ - printf(" Input:%x \n", image); - - // Reset image - mock_board_odout_write_ch(NOT_ACCESD_OD_CHANNEL); - mock_board_odout_write_data(BSP_OD1,NOT_ACCESD_OD_LEVEL); - mock_board_odout_write_data(BSP_OD2,NOT_ACCESD_OD_LEVEL); - mock_board_odout_write_data(BSP_OD3,NOT_ACCESD_OD_LEVEL); - mock_board_odout_write_data(BSP_OD4,NOT_ACCESD_OD_LEVEL); - mock_board_odout_write_data(BSP_OD5,NOT_ACCESD_OD_LEVEL); - mock_board_odout_write_data(BSP_OD6,NOT_ACCESD_OD_LEVEL); - - led_dsp_set_image(image); - - // Read set OD output image - uint8_t i1 = mock_board_odout_read_data(BSP_OD1); - uint8_t i2 = mock_board_odout_read_data(BSP_OD2); - uint8_t i3 = mock_board_odout_read_data(BSP_OD3); - uint8_t i4 = mock_board_odout_read_data(BSP_OD4); - uint8_t i5 = mock_board_odout_read_data(BSP_OD5); - uint8_t i6 = mock_board_odout_read_data(BSP_OD6); - - uint8_t out_image = 0x00; - if(i1==BSP_ODOUT_LOW) out_image |= 0x01; - if(i2==BSP_ODOUT_LOW) out_image |= 0x02; - if(i3==BSP_ODOUT_LOW) out_image |= 0x04; - if(i4==BSP_ODOUT_LOW) out_image |= 0x08; - if(i5==BSP_ODOUT_LOW) out_image |= 0x10; - if(i6==BSP_ODOUT_LOW) out_image |= 0x20; - - printf(" Output:%x \n", out_image); - printf("Expected:%x \n", exp_image); - - if(out_image==exp_image) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_led_dsp_set_image_test(void) -{ - printf("******************************************************\n"); - printf("void led_dsp_set_image(uint8_t image) \n"); - - int test_res; - int pass = 1; - - uint8_t image; - uint8_t exp_image; - - // Normal 1 - image = 0x00; - exp_image = 0x00; - test_res = ut_led_dsp_set_image(image, exp_image); - if(!test_res) pass = 0; - - // Normal 1 - image = 0x3F; - exp_image = 0x3F; - test_res = ut_led_dsp_set_image(image, exp_image); - if(!test_res) pass = 0; - - // Normal 1 - image = 0xAA; - exp_image = 0x2A; - test_res = ut_led_dsp_set_image(image, exp_image); - if(!test_res) pass = 0; - - // Normal 1 - image = 0x55; - exp_image = 0x15; - test_res = ut_led_dsp_set_image(image, exp_image); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_led_dsp_backligth_set(uint8_t percent, uint8_t exp_percent) -{ - printf(" Input:%d \n", percent); - - // Reset image - mock_board_odout_write_pwm(NOT_ACCESD_PERCENT); - - led_dsp_backligth_set(percent); - - // Read set OD output image - uint8_t pwm = mock_board_odout_read_pwm(); - - printf(" Output:%d \n", pwm); - printf("Expected:%d \n", exp_percent); - - if(pwm==exp_percent) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_led_dsp_backligth_set_test(void) -{ - printf("******************************************************\n"); - printf("void led_dsp_backligth_set(uint8_t percent) \n"); - - int test_res; - int pass = 1; - - uint8_t percent; - uint8_t exp_percent; - - // Normal 1 - percent = 0; - exp_percent = 0; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - // Normal 1 - percent = 25; - exp_percent = 25; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - // Normal 1 - percent = 50; - exp_percent = 50; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - // Normal 1 - percent = 75; - exp_percent = 75; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - // Normal 1 - percent = 100; - exp_percent = 100; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - // Normal 1 - percent = 200; - exp_percent = 200; - test_res = ut_led_dsp_backligth_set(percent, exp_percent); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_hw/ut_led_display.h b/firmware/tests/ut_hw/ut_led_display.h deleted file mode 100644 index 0f345c8..0000000 --- a/firmware/tests/ut_hw/ut_led_display.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UT_HW_LED_DISPLAY_H_ -#define UT_HW_LED_DISPLAY_H_ - -#include -#include - -int ut_led_dsp_set_image_test(void); -int ut_led_dsp_backligth_set_test(void); - -#endif /* UT_HW_LED_DISPLAY_H_ */ diff --git a/firmware/tests/ut_hw/ut_startup.c b/firmware/tests/ut_hw/ut_startup.c deleted file mode 100644 index 81ad1f8..0000000 --- a/firmware/tests/ut_hw/ut_startup.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "ut_startup.h" - -#include "..\mock_board\mock_board_setup.h" - -#include "..\..\src\hw\startup.h" - -static int ut_hw_startup(uint8_t exp_called) -{ - printf(" Input: \n"); - - mock_board_setup_reset_called(); - - hw_startup(); - - uint8_t called = mock_board_setup_read_called(); - - printf(" Output: Called:%d \n", called); - printf("Expected: Called:%d \n", exp_called); - - if(called==exp_called) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_hw_startup_test(void) -{ - printf("******************************************************\n"); - printf("void hw_startup(void) \n"); - - int test_res; - int pass = 1; - - uint8_t exp_called; - - // Normal 1 - exp_called = 1; - test_res = ut_hw_startup(exp_called); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_hw/ut_startup.h b/firmware/tests/ut_hw/ut_startup.h deleted file mode 100644 index 27e45af..0000000 --- a/firmware/tests/ut_hw/ut_startup.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_HW_STARTUP_H_ -#define UT_HW_STARTUP_H_ - -#include -#include - -int ut_hw_startup_test(void); - -#endif /* UT_HW_STARTUP_H_ */ diff --git a/firmware/tests/ut_logic/ut_coil.c b/firmware/tests/ut_logic/ut_coil.c deleted file mode 100644 index cc47e1f..0000000 --- a/firmware/tests/ut_logic/ut_coil.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "ut_coil.h" -#include "..\..\src\logic\coil.h" - -static int ut_coil_target(uint8_t force, uint8_t hbrake_act, int16_t exp_out) -{ - int16_t out = coil_target(force, hbrake_act); - - printf("Force:%d Handbrake:%d \n", force, hbrake_act); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_coil_target_test(void) -{ - printf("******************************************************\n"); - printf("int16_t coil_target(uint8_t force, uint8_t hbrake_act)\n"); - - int test_res; - int pass = 1; - - uint8_t force; - uint8_t hbrake; - - int16_t exp_out; - - force = 0; - hbrake = 0; - exp_out = 0; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 50; - hbrake = 0; - exp_out = COIL_LOCK_VOLTAGE/2; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 100; - hbrake = 0; - exp_out = COIL_LOCK_VOLTAGE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 255; - hbrake = 0; - exp_out = COIL_LOCK_VOLTAGE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 0; - hbrake = 1; - exp_out = COIL_TARGET_HANDBRAKE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 50; - hbrake = 1; - exp_out = COIL_TARGET_HANDBRAKE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 100; - hbrake = 1; - exp_out = COIL_TARGET_HANDBRAKE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - force = 255; - hbrake = 1; - exp_out = COIL_TARGET_HANDBRAKE; - test_res = ut_coil_target(force, hbrake, exp_out); - if(!test_res) pass = 0; - - return pass; -} - diff --git a/firmware/tests/ut_logic/ut_coil.h b/firmware/tests/ut_logic/ut_coil.h deleted file mode 100644 index c2109a6..0000000 --- a/firmware/tests/ut_logic/ut_coil.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_COIL_H_ -#define UT_COIL_H_ - -#include -#include - -int ut_coil_target_test(void); - -#endif /* UT_COIL_H_ */ diff --git a/firmware/tests/ut_logic/ut_display.c b/firmware/tests/ut_logic/ut_display.c deleted file mode 100644 index 7bf489e..0000000 --- a/firmware/tests/ut_logic/ut_display.c +++ /dev/null @@ -1,552 +0,0 @@ -#include "ut_display.h" -#include "..\..\src\logic\display.h" - -static const uint8_t BACKLIGHT_DIMM = DSP_BACKLIGHT_DIMM_PERCENT; -static const uint8_t BACKLIGHT_BRIGTH = DSP_BACKLIGHT_BRIGTH_PERCENT; - -static int ut_dsp_init_ctrl(dsp_ctrl_t* ctrl, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - - dsp_init_ctrl(ctrl); - - printf(" Output: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - printf("Expected: lock:%d act-image:%x \n", exp_ctrl->img_lock, exp_ctrl->act_img); - - if((ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_init_ctrl_test(void) -{ - printf("******************************************************\n"); - printf("void dsp_init_ctrl(dsp_ctrl_t* ctrl) \n"); - - int test_res; - int pass = 1; - - dsp_ctrl_t ctrl; - dsp_ctrl_t exp_ctrl; - - ctrl.img_lock = 0; - ctrl.act_img = 0; - exp_ctrl.img_lock = 0; - exp_ctrl.act_img = 0x00; - test_res = ut_dsp_init_ctrl(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - ctrl.img_lock = 1; - ctrl.act_img = 0xAA; - exp_ctrl.img_lock = 0; - exp_ctrl.act_img = 0x00; - test_res = ut_dsp_init_ctrl(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_set_lock(dsp_ctrl_t* ctrl, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - - dsp_set_lock(ctrl); - - printf(" Output: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - printf("Expected: lock:%d act-image:%x \n", exp_ctrl->img_lock, exp_ctrl->act_img); - - if((ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_set_lock_test(void) -{ - printf("******************************************************\n"); - printf("void dsp_set_lock(dsp_ctrl_t* ctrl)\n"); - - int test_res; - int pass = 1; - - dsp_ctrl_t ctrl; - dsp_ctrl_t exp_ctrl; - - ctrl.img_lock = 0; - ctrl.act_img = 0x18; - exp_ctrl.img_lock = 1; - exp_ctrl.act_img = 0x18; - test_res = ut_dsp_set_lock(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - ctrl.img_lock = 1; - ctrl.act_img = 0x3F; - exp_ctrl.img_lock = 1; - exp_ctrl.act_img = 0x3F; - test_res = ut_dsp_set_lock(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_reset_lock(dsp_ctrl_t* ctrl, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - - dsp_reset_lock(ctrl); - - printf(" Output: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - printf("Expected: lock:%d act-image:%x \n", exp_ctrl->img_lock, exp_ctrl->act_img); - - if((ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_reset_lock_test(void) -{ - printf("******************************************************\n"); - printf("void dsp_reset_lock(dsp_ctrl_t* ctrl)\n"); - - int test_res; - int pass = 1; - - dsp_ctrl_t ctrl; - dsp_ctrl_t exp_ctrl; - - ctrl.img_lock = 0; - ctrl.act_img = 0x18; - exp_ctrl.img_lock = 0; - exp_ctrl.act_img = 0x18; - test_res = ut_dsp_reset_lock(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - ctrl.img_lock = 1; - ctrl.act_img = 0x18; - exp_ctrl.img_lock = 0; - exp_ctrl.act_img = 0x18; - test_res = ut_dsp_reset_lock(&ctrl, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_img_percent(uint8_t value, dsp_style_t style, dsp_ctrl_t* ctrl, uint8_t exp_out, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: Percent:%d Style:%d lock:%d act-image:%x \n", value, style, ctrl->img_lock, ctrl->act_img); - - uint8_t out = dsp_img_percent(value, style, ctrl); - - printf(" Output: image:%x lock:%d act-image:%x \n", out, ctrl->img_lock, ctrl->act_img); - printf("Expected: image:%x lock:%d act-image:%x \n", exp_out, exp_ctrl->img_lock, exp_ctrl->act_img); - - if((out==exp_out)&&(ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_img_percent_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t dsp_img_percent(uint8_t value, dsp_style_t style, dsp_ctrl_t* ctrl)\n"); - - int test_res; - int pass = 1; - - uint8_t value; - dsp_style_t style; - dsp_ctrl_t ctrl; - uint8_t exp_out; - dsp_ctrl_t exp_ctrl; - - // Test lock - value = 50; - style = LED_DSP_BAR; - ctrl.img_lock = 1; - ctrl.act_img = 0x2A; - exp_out = 0x2A; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test styles - value = 50; - style = LED_DSP_BAR; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x07; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test styles - value = 50; - style = LED_DSP_DOT20; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x04; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test styles - value = 50; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x0C; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 0; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x01; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 10; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x03; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 20; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x02; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 30; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x06; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 40; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x04; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 50; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x0C; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 60; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x08; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 80; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x10; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 90; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x30; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test values - value = 110; - style = LED_DSP_DOT10; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x20; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_percent(value, style, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_img_raw(uint8_t image, dsp_ctrl_t* ctrl, uint8_t exp_out, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: Image:%x lock:%d act-image:%x \n", image, ctrl->img_lock, ctrl->act_img); - - uint8_t out = dsp_img_raw(image, ctrl); - - printf(" Output: image:%x lock:%d act-image:%x \n", out, ctrl->img_lock, ctrl->act_img); - printf("Expected: image:%x lock:%d act-image:%x \n", exp_out, exp_ctrl->img_lock, exp_ctrl->act_img); - - if((out==exp_out)&&(ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_img_raw_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t dsp_img_raw(int8_t image, dsp_ctrl_t* ctrl)\n"); - - int test_res; - int pass = 1; - - uint8_t image; - dsp_ctrl_t ctrl; - uint8_t exp_out; - dsp_ctrl_t exp_ctrl; - - // Test lock - image = 0x17; - ctrl.img_lock = 1; - ctrl.act_img = 0x2A; - exp_out = 0x2A; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_raw(image, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test normal - image = 0x17; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x17; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_raw(image, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test limit - image = 0xAA; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x2A; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_raw(image, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test limit - image = 0xCC; - ctrl.img_lock = 0; - ctrl.act_img = 0x00; - exp_out = 0x0C; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_img_raw(image, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_get_act_img(dsp_ctrl_t* ctrl, uint8_t exp_out, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: lock:%d act-image:%x \n", ctrl->img_lock, ctrl->act_img); - - uint8_t out = dsp_get_act_img(ctrl); - - printf(" Output: image:%x lock:%d act-image:%x \n", out, ctrl->img_lock, ctrl->act_img); - printf("Expected: image:%x lock:%d act-image:%x \n", exp_out, exp_ctrl->img_lock, exp_ctrl->act_img); - - if((out==exp_out)&&(ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_get_act_img_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t dsp_get_act_img(void)\n"); - - int test_res; - int pass = 1; - - dsp_ctrl_t ctrl; - uint8_t exp_out; - dsp_ctrl_t exp_ctrl; - - // Test - ctrl.img_lock = 0; - ctrl.act_img = 0x2A; - exp_out = 0x2A; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_get_act_img(&ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test - ctrl.img_lock = 1; - ctrl.act_img = 0x3F; - exp_out = 0x3F; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = exp_out; - test_res = ut_dsp_get_act_img(&ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_dsp_backlight(uint8_t dimm_act, dsp_ctrl_t* ctrl, uint8_t exp_out, dsp_ctrl_t* exp_ctrl) -{ - printf(" Input: dimm-act:%d lock:%d act-image:%x \n", dimm_act, ctrl->img_lock, ctrl->act_img); - - uint8_t out = dsp_backlight(dimm_act); - - printf(" Output: percent:%x lock:%d act-image:%x \n", out, ctrl->img_lock, ctrl->act_img); - printf("Expected: percent:%x lock:%d act-image:%x \n", exp_out, exp_ctrl->img_lock, exp_ctrl->act_img); - - if((out==exp_out)&&(ctrl->img_lock==exp_ctrl->img_lock)&&(ctrl->act_img==exp_ctrl->act_img)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_dsp_backlight_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t dsp_backlight(uint8_t dimm_act)\n"); - - int test_res; - int pass = 1; - - uint8_t dimm_act; - dsp_ctrl_t ctrl; - uint8_t exp_out; - dsp_ctrl_t exp_ctrl; - - // Test - dimm_act = 0; - ctrl.img_lock = 0; - ctrl.act_img = 0x2A; - exp_out = DSP_BACKLIGHT_BRIGTH_PERCENT; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = ctrl.act_img; - test_res = ut_dsp_backlight(dimm_act, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test - dimm_act = 1; - ctrl.img_lock = 0; - ctrl.act_img = 0x2A; - exp_out = DSP_BACKLIGHT_DIMM_PERCENT; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = ctrl.act_img; - test_res = ut_dsp_backlight(dimm_act, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test lock ignore - dimm_act = 0; - ctrl.img_lock = 1; - ctrl.act_img = 0x2A; - exp_out = DSP_BACKLIGHT_BRIGTH_PERCENT; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = ctrl.act_img; - test_res = ut_dsp_backlight(dimm_act, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - // Test lock ignore - dimm_act = 1; - ctrl.img_lock = 1; - ctrl.act_img = 0x2A; - exp_out = DSP_BACKLIGHT_DIMM_PERCENT; - exp_ctrl.img_lock = ctrl.img_lock; - exp_ctrl.act_img = ctrl.act_img; - test_res = ut_dsp_backlight(dimm_act, &ctrl, exp_out, &exp_ctrl); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_logic/ut_display.h b/firmware/tests/ut_logic/ut_display.h deleted file mode 100644 index d085252..0000000 --- a/firmware/tests/ut_logic/ut_display.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef UT_DISPLAY_H_ -#define UT_DISPLAY_H_ - -#include -#include -int ut_dsp_init_ctrl_test(void); - -int ut_dsp_set_lock_test(void); -int ut_dsp_reset_lock_test(void); - -int ut_dsp_img_percent_test(void); - -int ut_dsp_img_raw_test(void); - -int ut_dsp_get_act_img_test(void); - -int ut_dsp_backlight_test(void); - -#endif /* UT_DISPLAY_H_ */ diff --git a/firmware/tests/ut_logic/ut_force.c b/firmware/tests/ut_logic/ut_force.c deleted file mode 100644 index 5820e58..0000000 --- a/firmware/tests/ut_logic/ut_force.c +++ /dev/null @@ -1,170 +0,0 @@ -#include "ut_force.h" -#include "..\..\src\logic\force.h" - -static int ut_force_cycle_bmode(fbrake_mode_t bmode, fbrake_mode_t exp_out) -{ - fbrake_mode_t out = force_cycle_bmode(bmode); - - printf("Brake mode:%d\n", bmode); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_force_cycle_bmode_test(void) -{ - printf("******************************************************\n"); - printf("fbrake_mode_t force_cycle_bmode(fbrake_mode_t bmode)\n"); - - int test_res; - int pass = 1; - - fbrake_mode_t bmode; - - fbrake_mode_t exp_out; - - bmode = FORCE_BMODE_OPEN; - exp_out = FORCE_BMODE_KEEP; - test_res = ut_force_cycle_bmode(bmode,exp_out); - if(!test_res) pass = 0; - - bmode = FORCE_BMODE_KEEP; - exp_out = FORCE_BMODE_LOCK; - test_res = ut_force_cycle_bmode(bmode,exp_out); - if(!test_res) pass = 0; - - bmode = FORCE_BMODE_LOCK; - exp_out = FORCE_BMODE_OPEN; - test_res = ut_force_cycle_bmode(bmode,exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_force_next(uint8_t handbrake, uint8_t brakes, fbrake_mode_t bmode, uint8_t user_force, uint16_t hbarke_act_time, uint8_t exp_out) -{ - uint8_t out = force_next(handbrake, brakes, bmode, user_force, hbarke_act_time); - - printf("Handbrake:%d Brakes:%d Brake-Mode:%d User-Force:%d Handbrake-time:%d\n", handbrake, brakes, bmode, user_force, hbarke_act_time); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_force_next_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t force_next(uint8_t handbrake, uint8_t brakes, fbrake_mode_t bmode, uint8_t user_force, uint16_t hbarke_act_time)\n"); - - int test_res; - int pass = 1; - - uint8_t handbrake; - uint8_t brakes; - fbrake_mode_t bmode; - uint8_t user_force; - uint16_t hbarke_act_time; - - uint8_t exp_out; - - handbrake = 0; - brakes = 0; - bmode = FORCE_BMODE_OPEN; - user_force = 0; - hbarke_act_time = 0; - exp_out = 0; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 0; - brakes = 0; - bmode = FORCE_BMODE_OPEN; - user_force = 99; - hbarke_act_time = 0; - exp_out = 99; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 0; - brakes = 1; - bmode = FORCE_BMODE_OPEN; - user_force = 99; - hbarke_act_time = 0; - exp_out = 0; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 0; - brakes = 1; - bmode = FORCE_BMODE_KEEP; - user_force = 99; - hbarke_act_time = 0; - exp_out = 99; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 0; - brakes = 1; - bmode = FORCE_BMODE_LOCK; - user_force = 99; - hbarke_act_time = 0; - exp_out = 100; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 1; - brakes = 1; - bmode = FORCE_BMODE_LOCK; - user_force = 99; - hbarke_act_time = 0; - exp_out = 0; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 1; - brakes = 1; - bmode = FORCE_BMODE_LOCK; - user_force = 99; - hbarke_act_time = FORCE_MAX_HBRAKE_HOLD_TIME-1; - exp_out = 0; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 1; - brakes = 1; - bmode = FORCE_BMODE_LOCK; - user_force = 99; - hbarke_act_time = FORCE_MAX_HBRAKE_HOLD_TIME; - exp_out = 100; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - handbrake = 1; - brakes = 1; - bmode = FORCE_BMODE_LOCK; - user_force = 99; - hbarke_act_time = FORCE_MAX_HBRAKE_HOLD_TIME+1; - exp_out = 100; - test_res = ut_force_next(handbrake, brakes, bmode, user_force, hbarke_act_time, exp_out); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_logic/ut_force.h b/firmware/tests/ut_logic/ut_force.h deleted file mode 100644 index e971dd5..0000000 --- a/firmware/tests/ut_logic/ut_force.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UT_FORCE_H_ -#define UT_FORCE_H_ - -#include -#include - -int ut_force_cycle_bmode_test(void); -int ut_force_next_test(void); - -#endif /* UT_FORCE_H_ */ diff --git a/firmware/tests/ut_logic/ut_pot.c b/firmware/tests/ut_logic/ut_pot.c deleted file mode 100644 index 200cbb9..0000000 --- a/firmware/tests/ut_logic/ut_pot.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "ut_pot.h" -#include "..\..\src\logic\pot.h" - -static int ut_pot_mv_to_percent(uint16_t value, pot_cfg_t* cfg, uint8_t exp_out) -{ - uint8_t out = pot_mv_to_percent(value, cfg); - - printf("Value:%d Reference:%d Deadband:%d \n", value, cfg->reference, cfg->deadband); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_pot_mv_to_percent_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t pot_mv_to_percent(uint16_t value, pot_cfg_t* cfg)\n"); - - int test_res; - int pass = 1; - - uint16_t value; - pot_cfg_t cfg; - - uint8_t exp_out; - - value = 0; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 0; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 499; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 0; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 500; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 0; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 1500; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 25; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 2500; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 50; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 3500; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 75; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 4500; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 100; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 4501; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 100; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - value = 5000; - cfg.deadband = 500; - cfg.reference = 5000; - exp_out = 100; - test_res = ut_pot_mv_to_percent(value, &cfg, exp_out); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_logic/ut_pot.h b/firmware/tests/ut_logic/ut_pot.h deleted file mode 100644 index 0fb97d0..0000000 --- a/firmware/tests/ut_logic/ut_pot.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_POT_H_ -#define UT_POT_H_ - -#include -#include - -int ut_pot_mv_to_percent_test(void); - -#endif /* UT_POT_H_ */ diff --git a/firmware/tests/ut_logic/ut_user_force.c b/firmware/tests/ut_logic/ut_user_force.c deleted file mode 100644 index b2525e8..0000000 --- a/firmware/tests/ut_logic/ut_user_force.c +++ /dev/null @@ -1,419 +0,0 @@ -#include "ut_user_force.h" -#include "..\..\src\logic\user_force.h" - -static int ut_user_force_btn(uint8_t prev_force, uint8_t up_act, uint8_t down_act, uint8_t delta, uint8_t exp_out) -{ - uint8_t out = user_force_btn(prev_force, up_act, down_act, delta); - - printf("Prev-Force:%d Up-act:%d Down-act:%d Delta:%d\n", prev_force, up_act, down_act, delta); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_user_force_btn_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t user_force_btn(uint8_t prev_force, uint8_t up_act, uint8_t down_act, uint8_t delta)\n"); - - int test_res; - int pass = 1; - - uint8_t prev_force; - uint8_t up_act; - uint8_t down_act; - uint8_t delta; - - uint8_t exp_out; - - // No change - prev_force = 0; - up_act = 0; - down_act = 0; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MIN_PERCENT-1; - up_act = 0; - down_act = 0; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - up_act = 0; - down_act = 0; - delta = 5; - exp_out = 50; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MAX_PERCENT+1; - up_act = 0; - down_act = 0; - delta = 5; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 0; - down_act = 0; - delta = 5; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Start 0 - prev_force = 0; - up_act = 1; - down_act = 0; - delta = 5; - exp_out = USER_FORCE_MIN_PERCENT; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 0; - up_act = 0; - down_act = 1; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 0; - up_act = 1; - down_act = 1; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 0; - up_act = 1; - down_act = 0; - delta = USER_FORCE_MIN_PERCENT+1; - exp_out = USER_FORCE_MIN_PERCENT+1; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Start 100 - prev_force = 100; - up_act = 1; - down_act = 0; - delta = 5; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 0; - down_act = 1; - delta = 5; - exp_out = USER_FORCE_MAX_PERCENT; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 1; - down_act = 1; - delta = 5; - exp_out = USER_FORCE_MAX_PERCENT; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 0; - down_act = 1; - delta = 100-USER_FORCE_MAX_PERCENT+1; - exp_out = USER_FORCE_MAX_PERCENT-1; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Start 50 - prev_force = 50; - up_act = 1; - down_act = 0; - delta = 5; - exp_out = 55; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - up_act = 0; - down_act = 1; - delta = 5; - exp_out = 45; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - up_act = 1; - down_act = 1; - delta = 5; - exp_out = 45; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - up_act = 1; - down_act = 0; - delta = 10; - exp_out = 60; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - up_act = 0; - down_act = 1; - delta = 10; - exp_out = 40; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Start MIN_PERCENT+1 - prev_force = USER_FORCE_MIN_PERCENT+1; - up_act = 1; - down_act = 0; - delta = 5; - exp_out = prev_force+delta; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MIN_PERCENT+1; - up_act = 0; - down_act = 1; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MIN_PERCENT+1; - up_act = 1; - down_act = 1; - delta = 5; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Start MAX_PERCENT-1 - prev_force = USER_FORCE_MAX_PERCENT-1; - up_act = 1; - down_act = 0; - delta = 5; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MAX_PERCENT-1; - up_act = 0; - down_act = 1; - delta = 5; - exp_out = prev_force-delta; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = USER_FORCE_MAX_PERCENT-1; - up_act = 1; - down_act = 1; - delta = 5; - exp_out = prev_force-delta; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - // Up down seq - prev_force = 0; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 20; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 20; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 40; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 40; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 60; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 60; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 80; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 80; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 1; - down_act = 0; - delta = 20; - exp_out = 100; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 100; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 80; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 80; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 60; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 60; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 40; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 40; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 20; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 20; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - prev_force = 0; - up_act = 0; - down_act = 1; - delta = 20; - exp_out = 0; - test_res = ut_user_force_btn(prev_force, up_act, down_act, delta, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_user_force_pot(uint8_t prev_force, uint8_t pot, uint8_t hyst, uint8_t exp_out) -{ - uint8_t out = user_force_pot(prev_force, pot, hyst); - - printf("Prev-Force:%d Pot-percent:%d Hysteresis:%d \n", prev_force, pot, hyst); - printf("Output:%d Expected:%d\n", out, exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_user_force_pot_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t user_force_pot(uint8_t prev_force, uint8_t pot, uint8_t hyst)\n"); - - int test_res; - int pass = 1; - - uint8_t prev_force; - uint8_t pot; - uint8_t hyst; - - uint8_t exp_out; - - prev_force = 50; - pot = 0; - hyst = 10; - exp_out = 0; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = USER_FORCE_MIN_PERCENT-1; - hyst = 10; - exp_out = 0; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = USER_FORCE_MIN_PERCENT; - hyst = 10; - exp_out = USER_FORCE_MIN_PERCENT; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = 50; - hyst = 10; - exp_out = 50; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = USER_FORCE_MAX_PERCENT; - hyst = 10; - exp_out = USER_FORCE_MAX_PERCENT; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = USER_FORCE_MAX_PERCENT+1; - hyst = 10; - exp_out = 100; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - prev_force = 50; - pot = 100; - hyst = 10; - exp_out = 100; - test_res = ut_user_force_pot(prev_force, pot, hyst, exp_out); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_logic/ut_user_force.h b/firmware/tests/ut_logic/ut_user_force.h deleted file mode 100644 index cbaf5a6..0000000 --- a/firmware/tests/ut_logic/ut_user_force.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UT_USER_FORCE_H_ -#define UT_USER_FORCE_H_ - -#include -#include - -int ut_user_force_btn_test(void); -int ut_user_force_pot_test(void); - -#endif /* UT_USER_FORCE_H_ */ diff --git a/firmware/tests/ut_utils/ut_fault.c b/firmware/tests/ut_utils/ut_fault.c deleted file mode 100644 index 0abcdfd..0000000 --- a/firmware/tests/ut_utils/ut_fault.c +++ /dev/null @@ -1,951 +0,0 @@ -#include "ut_fault.h" - -#include "..\..\src\hw\board\utils\faults.h" - -static int ut_fault_process(fault_t* fault, uint8_t w_trig, uint8_t f_trig, fault_cfg_t* cfg, uint8_t exp_out, fault_t* exp_out_fault, fault_cfg_t* exp_out_cfg) -{ - printf("W-Trig:%d F-trig:%d \n", w_trig, f_trig); - printf("Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - printf("Delay:%d WtoF:%d \n", cfg->delay, cfg->wtof); - - uint8_t out = fault_process(fault, w_trig, f_trig, cfg); - - printf("Output:%d Expected:%d\n", out, exp_out); - printf(" Output: Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - printf("Expected: Severity:%d W-time:%d F-time:%d \n", exp_out_fault->severity, exp_out_fault->w_time, exp_out_fault->f_time); - printf("Delay:%d WtoF:%d \n", cfg->delay, cfg->wtof); - - if((out==exp_out)&&(fault->severity==exp_out_fault->severity)&&(fault->w_time==exp_out_fault->w_time)&&(fault->f_time==exp_out_fault->f_time)&&(cfg->delay==exp_out_cfg->delay)&&(cfg->wtof==exp_out_cfg->wtof)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_fault_process_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t fault_process(fault_t* fault, uint8_t w_trig, uint8_t f_trig, fault_cfg_t* cfg)\n"); - - int test_res; - int pass = 1; - - fault_t fault; - uint8_t w_trig; - uint8_t f_trig; - fault_cfg_t cfg; - - uint8_t exp_out; - fault_t exp_out_fault; - fault_cfg_t exp_out_cfg; - - // NORMAL NO FAULT CONDITION ************************************************************************* - // No change test, start form OK - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // STARTING CONDITIONS ************************************************************************* - // Warning trigger test, start form OK - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fault trigger test, start form OK - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Warning and Fault trigger test, start form OK - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Wrong start severity WARNING, with no triggers - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Wrong start severity FAULT, with no triggers - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Wrong start times, with no triggers - fault.severity = FAULT_LVL_OK; - fault.w_time = 78; - fault.f_time = 56; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - - // TIME INCREASE TESTS - SECOND ITERATION WITH TRIGGER ACTIVE ************************************************************************* - // Warning time increase with continuing warning condition - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 45; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 46; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fault and warning time increase with continuing fault only condition - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 78; - fault.f_time = 23; - w_trig = 0; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 79; - exp_out_fault.f_time = 24; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fault and warning time increase with continuing both triggers condition - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 57; - fault.f_time = 12; - w_trig = 1; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 58; - exp_out_fault.f_time = 13; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Time increase and transition to fault with both triggers - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 1; - exp_out_fault.f_time = 1; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Warning time increase and transition to fault with only fault trigger - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 1; - exp_out_fault.f_time = 1; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Time saturation - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 65535; - fault.f_time = 65535; - w_trig = 1; - f_trig = 1; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 65535; - exp_out_fault.f_time = 65535; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to warning and time increase - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 784; - fault.f_time = 457; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 785; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to OK and time reset from fault - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 784; - fault.f_time = 457; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to OK and time reset from warning - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 479; - fault.f_time = 0; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // FAULT DELAY TESTS ************************************************************************* - // Not transition to fault - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not transition to fault second time - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 1; - exp_out_fault.f_time = 1; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not transition to fault before limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 128; - fault.f_time = 98; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 129; - exp_out_fault.f_time = 99; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Transition to fault at limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 358; - fault.f_time = 99; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 359; - exp_out_fault.f_time = 100; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Transition to fault after limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 786; - fault.f_time = 100; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 787; - exp_out_fault.f_time = 101; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Continued fault condition - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 101; - fault.f_time = 101; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 102; - exp_out_fault.f_time = 102; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to warning - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 101; - fault.f_time = 101; - w_trig = 1; - f_trig = 0; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 102; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to OK - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 101; - fault.f_time = 101; - w_trig = 0; - f_trig = 0; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Allready was fault condition, with lower time - fixes to correct state - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 45; - fault.f_time = 45; - w_trig = 1; - f_trig = 1; - cfg.delay = 100; - cfg.wtof = 0; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 46; - exp_out_fault.f_time = 46; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // WARNING TO FAULT TRANSITION TESTS ************************************************************************* - // Not transition to fault - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not transition to fault - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 1; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not transition to fault before limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 198; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 199; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not Transition to fault at limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 199; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 200; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Transition to fault after limit - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 200; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 201; - exp_out_fault.f_time = 1; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Continued fault - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 201; - fault.f_time = 1; - w_trig = 1; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 202; - exp_out_fault.f_time = 2; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Fallback to ok - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 247; - fault.f_time = 0; - w_trig = 0; - f_trig = 0; - cfg.delay = 0; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // WARNING TO FAULT TRANSITION WITH FAULT DELAY TESTS ************************************************************************* - // Not transition to fault - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Not transition to fault - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 1; - f_trig = 1; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - w_trig = 0; - f_trig = 1; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Transition to fault at delay - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 49; - fault.f_time = 49; - w_trig = 1; - f_trig = 1; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 50; - exp_out_fault.f_time = 50; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // No Transition to fault at wtof - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 200; - fault.f_time = 0; - w_trig = 1; - f_trig = 0; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 201; - exp_out_fault.f_time = 1; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // No Transition to fault before wtof + delay - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 248; - fault.f_time = 48; - w_trig = 1; - f_trig = 0; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 0; - exp_out_fault.severity = FAULT_LVL_WARNING; - exp_out_fault.w_time = 249; - exp_out_fault.f_time = 49; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Transition to fault before wtof + delay - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 249; - fault.f_time = 49; - w_trig = 1; - f_trig = 0; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 250; - exp_out_fault.f_time = 50; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Continued wtof fault condition - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 250; - fault.f_time = 50; - w_trig = 1; - f_trig = 0; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 251; - exp_out_fault.f_time = 51; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - // Continued wtof fault condition, without warning trigger - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 350; - fault.f_time = 150; - w_trig = 0; - f_trig = 1; - cfg.delay = 50; - cfg.wtof = 200; - exp_out = 1; - exp_out_fault.severity = FAULT_LVL_FAULT; - exp_out_fault.w_time = 351; - exp_out_fault.f_time = 151; - exp_out_cfg.delay = cfg.delay; - exp_out_cfg.wtof = cfg.wtof; - test_res = ut_fault_process(&fault, w_trig, f_trig, &cfg, exp_out, &exp_out_fault, &exp_out_cfg); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_fault_is_active(fault_t* fault, uint8_t exp_out, fault_t* exp_out_fault) -{ - printf("Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - - uint8_t out = fault_is_active(fault); - - printf("Output:%d Expected:%d\n", out, exp_out); - printf(" Output: Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - printf("Expected: Severity:%d W-time:%d F-time:%d \n", exp_out_fault->severity, exp_out_fault->w_time, exp_out_fault->f_time); - - if((out==exp_out)&&(fault->severity==exp_out_fault->severity)&&(fault->w_time==exp_out_fault->w_time)&&(fault->f_time==exp_out_fault->f_time)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_fault_is_active_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t fault_is_active(fault_t* fault)\n"); - - int test_res; - int pass = 1; - - fault_t fault; - - uint8_t exp_out; - fault_t exp_out_fault; - - fault.severity = FAULT_LVL_OK; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 0; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_active(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 0; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_active(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 1; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_active(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_fault_is_warning(fault_t* fault, uint8_t exp_out, fault_t* exp_out_fault) -{ - printf("Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - - uint8_t out = fault_is_warning(fault); - - printf("Output:%d Expected:%d\n", out, exp_out); - printf(" Output: Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - printf("Expected: Severity:%d W-time:%d F-time:%d \n", exp_out_fault->severity, exp_out_fault->w_time, exp_out_fault->f_time); - - if((out==exp_out)&&(fault->severity==exp_out_fault->severity)&&(fault->w_time==exp_out_fault->w_time)&&(fault->f_time==exp_out_fault->f_time)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_fault_is_warning_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t fault_is_warning(fault_t* fault)\n"); - - int test_res; - int pass = 1; - - fault_t fault; - - uint8_t exp_out; - fault_t exp_out_fault; - - fault.severity = FAULT_LVL_OK; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 0; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_warning(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 1; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_warning(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 500; - fault.f_time = 300; - exp_out = 1; - exp_out_fault.severity = fault.severity; - exp_out_fault.w_time = fault.w_time; - exp_out_fault.f_time = fault.f_time; - test_res = ut_fault_is_warning(&fault, exp_out, &exp_out_fault); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_fault_reset(fault_t* fault, fault_t* exp_out_fault) -{ - printf("Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - - fault_reset(fault); - - printf(" Output: Severity:%d W-time:%d F-time:%d \n", fault->severity, fault->w_time, fault->f_time); - printf("Expected: Severity:%d W-time:%d F-time:%d \n", exp_out_fault->severity, exp_out_fault->w_time, exp_out_fault->f_time); - - if((fault->severity==exp_out_fault->severity)&&(fault->w_time==exp_out_fault->w_time)&&(fault->f_time==exp_out_fault->f_time)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_fault_reset_test(void) -{ - printf("******************************************************\n"); - printf("void fault_reset(fault_t* fault)\n"); - - int test_res; - int pass = 1; - - fault_t fault; - - fault_t exp_out_fault; - - fault.severity = FAULT_LVL_OK; - fault.w_time = 0; - fault.f_time = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - test_res = ut_fault_reset(&fault, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_WARNING; - fault.w_time = 500; - fault.f_time = 0; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - test_res = ut_fault_reset(&fault, &exp_out_fault); - if(!test_res) pass = 0; - - fault.severity = FAULT_LVL_FAULT; - fault.w_time = 1000; - fault.f_time = 500; - exp_out_fault.severity = FAULT_LVL_OK; - exp_out_fault.w_time = 0; - exp_out_fault.f_time = 0; - test_res = ut_fault_reset(&fault, &exp_out_fault); - if(!test_res) pass = 0; - - return pass; -} - diff --git a/firmware/tests/ut_utils/ut_fault.h b/firmware/tests/ut_utils/ut_fault.h deleted file mode 100644 index 1024805..0000000 --- a/firmware/tests/ut_utils/ut_fault.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef UT_FAULTS_H_ -#define UT_FAULTS_H_ - -#include -#include - -int ut_fault_process_test(void); -int ut_fault_is_active_test(void); -int ut_fault_is_warning_test(void); -int ut_fault_reset_test(void); - -#endif /* UT_FAULTS_H_ */ diff --git a/firmware/tests/ut_utils/ut_fuses.c b/firmware/tests/ut_utils/ut_fuses.c deleted file mode 100644 index 03b7138..0000000 --- a/firmware/tests/ut_utils/ut_fuses.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "ut_fuses.h" - -#include "..\..\src\hw\board\utils\fuses.h" - -static int ut_fuse_reset(fuse_t* fuse, fuse_t* exp_out_fuse) -{ - printf("State:%d Timer:%d Count:%d \n", fuse->state, fuse->timer, fuse->count); - - fuse_reset(fuse); - - printf(" Output: State:%d Timer:%d Count:%d \n", fuse->state, fuse->timer, fuse->count); - printf("Expected: State:%d Timer:%d Count:%d \n", exp_out_fuse->state, exp_out_fuse->timer, exp_out_fuse->count); - - if((fuse->state==exp_out_fuse->state)&&(fuse->timer==exp_out_fuse->timer)&&(fuse->count==exp_out_fuse->count)) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_fuse_reset_test(void) -{ - printf("******************************************************\n"); - printf("void fuse_reset(fuse_t* fuse)\n"); - - int test_res; - int pass = 1; - - fuse_t fuse; - - fuse_t exp_out_fuse; - - // No change test - fuse.state = FUSE_OFF; - fuse.timer = 0; - fuse.count = 0; - exp_out_fuse.state = FUSE_OFF; - exp_out_fuse.timer = 0; - exp_out_fuse.count = 0; - test_res = ut_fuse_reset(&fuse, &exp_out_fuse); - if(!test_res) pass = 0; - - // No change test - fuse.state = FUSE_ACTIVE; - fuse.timer = 1254; - fuse.count = 124; - exp_out_fuse.state = FUSE_OFF; - exp_out_fuse.timer = 0; - exp_out_fuse.count = 0; - test_res = ut_fuse_reset(&fuse, &exp_out_fuse); - if(!test_res) pass = 0; - - // No change test - fuse.state = FUSE_COOLDOWN; - fuse.timer = 4578; - fuse.count = 14; - exp_out_fuse.state = FUSE_OFF; - exp_out_fuse.timer = 0; - exp_out_fuse.count = 0; - test_res = ut_fuse_reset(&fuse, &exp_out_fuse); - if(!test_res) pass = 0; - - // No change test - fuse.state = FUSE_RETRY; - fuse.timer = 0; - fuse.count = 0; - exp_out_fuse.state = FUSE_OFF; - exp_out_fuse.timer = 0; - exp_out_fuse.count = 0; - test_res = ut_fuse_reset(&fuse, &exp_out_fuse); - if(!test_res) pass = 0; - - return pass; -} diff --git a/firmware/tests/ut_utils/ut_fuses.h b/firmware/tests/ut_utils/ut_fuses.h deleted file mode 100644 index 87bd1b7..0000000 --- a/firmware/tests/ut_utils/ut_fuses.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef UT_FUSES_H_ -#define UT_FUSES_H_ - -#include -#include - -int ut_fuse_reset_test(void); - -#endif /* UT_FUSES_H_ */ diff --git a/firmware/tests/ut_utils/ut_utils.c b/firmware/tests/ut_utils/ut_utils.c deleted file mode 100644 index b260687..0000000 --- a/firmware/tests/ut_utils/ut_utils.c +++ /dev/null @@ -1,1334 +0,0 @@ -#include "ut_utils.h" - -#include "..\..\src\hw\board\utils\utils.h" - -static int ut_util_invert_8b(uint8_t x, uint8_t exp_out) -{ - printf(" Input:%d\n", x); - - uint8_t out = util_invert_8b(x); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_invert_8b_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t util_invert_8b(uint8_t x)\n"); - - int test_res; - int pass = 1; - - uint8_t inp; - uint8_t exp_out; - - inp = 0; - exp_out = 1; - test_res = ut_util_invert_8b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 1; - exp_out = 0; - test_res = ut_util_invert_8b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 257; - exp_out = 0; - test_res = ut_util_invert_8b(inp, exp_out); - if(!test_res) pass = 0; - - inp = -1; - exp_out = 0; - test_res = ut_util_invert_8b(inp, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_add_8b(uint8_t x, uint8_t y, uint8_t exp_out) -{ - printf(" Input: x:%d y:%d\n", x, y); - - uint8_t out = util_sat_add_8b(x,y); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_add_8b_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t util_sat_add_8b(uint8_t x, uint8_t y)\n"); - - int test_res; - int pass = 1; - - uint8_t inp_x; - uint8_t inp_y; - uint8_t exp_out; - - inp_x = 0; - inp_y = 0; - exp_out = 0; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 5; - inp_y = 6; - exp_out = 11; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 4; - inp_y = 9; - exp_out = 13; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 254; - inp_y = 1; - exp_out = 255; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 1; - inp_y = 254; - exp_out = 255; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 1; - inp_y = 255; - exp_out = 255; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 255; - inp_y = 1; - exp_out = 255; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 255; - inp_y = 255; - exp_out = 255; - test_res = ut_util_sat_add_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - return pass; -} - - -static int ut_util_sat_subtract_8b(uint8_t x, uint8_t y, uint8_t exp_out) -{ - printf(" Input: x:%d y:%d\n", x, y); - - uint8_t out = util_sat_subtract_8b(x,y); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_subtract_8b_test(void) -{ - printf("******************************************************\n"); - printf("uint8_t util_sat_subtract_8b(uint8_t x, uint8_t y)\n"); - - int test_res; - int pass = 1; - - uint8_t inp_x; - uint8_t inp_y; - uint8_t exp_out; - - inp_x = 0; - inp_y = 0; - exp_out = 0; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 50; - inp_y = 10; - exp_out = 40; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 70; - inp_y = 0; - exp_out = 70; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 60; - inp_y = 50; - exp_out = 10; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 10; - inp_y = 50; - exp_out = 0; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 0; - inp_y = 255; - exp_out = 0; - test_res = ut_util_sat_subtract_8b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_add_16b(uint16_t x, uint16_t y, uint16_t exp_out) -{ - printf(" Input: x:%d y:%d\n", x, y); - - uint16_t out = util_sat_add_16b(x,y); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_util_sat_add_16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_sat_add_16b(uint16_t x, uint16_t y)\n"); - - int test_res; - int pass = 1; - - uint16_t inp_x; - uint16_t inp_y; - uint16_t exp_out; - - inp_x = 0; - inp_y = 0; - exp_out = 0; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 10; - inp_y = 15; - exp_out = 25; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 25; - inp_y = 35; - exp_out = 60; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 0; - inp_y = 0xFFFF; - exp_out = 0xFFFF; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 100; - inp_y = 0xFFFF; - exp_out = 0xFFFF; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 45867; - inp_y = 47841; - exp_out = 0xFFFF; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 0xFFFF; - inp_y = 0xFFFF; - exp_out = 0xFFFF; - test_res = ut_util_sat_add_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_subtract_16b(uint16_t x, uint16_t y, uint16_t exp_out) -{ - printf(" Input: x:%d y:%d\n", x, y); - - uint16_t out = util_sat_subtract_16b(x,y); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_subtract_16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_sat_subtract_16b(uint16_t x, uint16_t y)\n"); - - int test_res; - int pass = 1; - - uint16_t inp_x; - uint16_t inp_y; - uint16_t exp_out; - - inp_x = 0; - inp_y = 0; - exp_out = 0; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 50; - inp_y = 10; - exp_out = 40; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 70; - inp_y = 0; - exp_out = 70; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 60; - inp_y = 50; - exp_out = 10; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 10; - inp_y = 50; - exp_out = 0; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 401; - inp_y = 0x3FFF; - exp_out = 0; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 0; - inp_y = 0x7FFF; - exp_out = 0; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - inp_x = 0; - inp_y = 0xFFFF; - exp_out = 0; - test_res = ut_util_sat_subtract_16b(inp_x, inp_y, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_limit_u32b_to_u16b(uint32_t inp, uint16_t exp_out) -{ - printf(" Input: %d\n", inp); - - uint16_t out = util_limit_u32b_to_u16b(inp); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_limit_u32b_to_u16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_limit_u32b_to_u16b(uint32_t in)\n"); - - int test_res; - int pass = 1; - - uint32_t inp; - uint16_t exp_out; - - inp = 0; - exp_out = 0; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 0x0000FFFE; - exp_out = 0xFFFE; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 0x0000FFFF; - exp_out = 0xFFFF; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 0x0001FFFF; - exp_out = 0xFFFF; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 0x7FFFFFFF; - exp_out = 0xFFFF; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 0xFFFFFFFF; - exp_out = 0xFFFF; - test_res = ut_util_limit_u32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_limit_s32b_to_u16b(int32_t inp, uint16_t exp_out) -{ - printf(" Input: %d\n", inp); - - uint16_t out = util_limit_s32b_to_u16b(inp); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_limit_s32b_to_u16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_limit_s32b_to_u16b(int32_t in)\n"); - - int test_res; - int pass = 1; - - int32_t inp; - uint16_t exp_out; - - inp = 0; - exp_out = 0; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = -1; - exp_out = 0; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = -32768; - exp_out = 0; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = -2147483648; - exp_out = 0; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 1; - exp_out = 1; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 32767; - exp_out = 32767; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 65535; - exp_out = 0xFFFF; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 1000000000; - exp_out = 0xFFFF; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - inp = 2147483647; - exp_out = 0xFFFF; - test_res = ut_util_limit_s32b_to_u16b(inp, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset, uint16_t exp_out) -{ - printf(" Input: Raw:%d Mul:%d Div:%d Offset:%d\n", raw, mul, div, offset); - uint16_t out = util_convert_muldivoff(raw, mul, div, offset); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_convert_muldivoff_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset) \n"); - - int test_res; - int pass = 1; - - uint16_t raw; - uint8_t mul; - uint8_t div; - int16_t offset; - uint16_t exp_out; - - // Normal case - raw = 100; - mul = 1; - div = 1; - offset = 100; - exp_out = 200; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Normal case - raw = 0; - mul = 1; - div = 1; - offset = 0; - exp_out = 0; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // max raw - raw = 0xFFFF; - mul = 1; - div = 1; - offset = 0; - exp_out = 0xFFFF; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Saturation + - raw = 40000; - mul = 1; - div = 1; - offset = 30000; - exp_out = 0xFFFF; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Saturation - - raw = 20000; - mul = 1; - div = 1; - offset = -30000; - exp_out = 0; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Multiplier - raw = 10; - mul = 30; - div = 1; - offset = 200; - exp_out = 500; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Divider - raw = 10; - mul = 30; - div = 6; - offset = 200; - exp_out = 250; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Zero mul - raw = 10; - mul = 0; - div = 6; - offset = 99; - exp_out = 99; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Zero div - raw = 10; - mul = 2; - div = 0; - offset = 80; - exp_out = 100; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - // Zero div - raw = 0xFFFE; - mul = 255; - div = 255; - offset = -1; - exp_out = 0xFFFD; - test_res = ut_util_convert_muldivoff(raw, mul, div, offset, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_mul_kilo(uint16_t xk, uint16_t yk, uint16_t exp_out) -{ - printf(" Input: X:%d Y:%d \n", xk, yk); - - uint16_t out = util_sat_mul_kilo(xk, yk); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_mul_kilo_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_sat_mul_kilo(uint16_t xk, uint16_t yk) \n"); - - int test_res; - int pass = 1; - - uint16_t xk; - uint16_t yk; - uint16_t exp_out; - - // Normal case - xk = 1000; - yk = 1000; - exp_out = 1000; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - // Normal case - xk = 60000; - yk = 1; - exp_out = 60; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - // Normal case - xk = 2; - yk = 30000; - exp_out = 60; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - // Normal case - xk = 0xFFFF; - yk = 0xFFFF; - exp_out = 0xFFFF; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - // Normal case - xk = 8095; - yk = 8095; - exp_out = 65529; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - // Saturation - xk = 10000; - yk = 20000; - exp_out = 0xFFFF; - test_res = ut_util_sat_mul_kilo(xk, yk, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_div_kilo(uint16_t top, uint16_t bot, uint16_t exp_out) -{ - printf(" Input: Top:%d Bottom:%d \n", top, bot); - - uint16_t out = util_sat_div_kilo(top, bot); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_div_kilo_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_sat_div_kilo(uint16_t top, uint16_t bot) \n"); - - int test_res; - int pass = 1; - - uint16_t top; - uint16_t bot; - uint16_t exp_out; - - // Normal case - top = 1000; - bot = 1000; - exp_out = 1000; - test_res = ut_util_sat_div_kilo(top, bot, exp_out); - if(!test_res) pass = 0; - - // Normal case - top = 12000; - bot = 6000; - exp_out = 2000; - test_res = ut_util_sat_div_kilo(top, bot, exp_out); - if(!test_res) pass = 0; - - // Saturation - top = 40000; - bot = 500; - exp_out = 0xFFFF; - test_res = ut_util_sat_div_kilo(top, bot, exp_out); - if(!test_res) pass = 0; - - // Zero - top = 40000; - bot = 0; - exp_out = 0xFFFF; - test_res = ut_util_sat_div_kilo(top, bot, exp_out); - if(!test_res) pass = 0; - - // Zero - top = 0; - bot = 2000; - exp_out = 0; - test_res = ut_util_sat_div_kilo(top, bot, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_sat_ratio_16b(uint16_t top, uint16_t bot, uint16_t exp_out) -{ - printf(" Input: Top:%d Bottom:%d \n", top, bot); - - uint16_t out = util_sat_ratio_16b(top, bot); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_sat_ratio_16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_sat_ratio_16b(uint16_t top, uint16_t bot) \n"); - - int test_res; - int pass = 1; - - uint16_t top; - uint16_t bot; - uint16_t exp_out; - - // Normal case - top = 1000; - bot = 1000; - exp_out = 0xFFFF; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - // Normal case - top = 0xFFFF; - bot = 0xFFFF; - exp_out = 0xFFFF; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - // Normal case - top = 6000; - bot = 12000; - exp_out = 0x7FFF; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - // Normal case - top = 11000; - bot = 44000; - exp_out = 0x3FFF; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - // Zero - top = 0; - bot = 212; - exp_out = 0; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - // Zero - top = 158; - bot = 0; - exp_out = 0xFFFF; - test_res = ut_util_sat_ratio_16b(top, bot, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_percent_to_16b(uint8_t percent, uint16_t exp_out) -{ - printf(" Input: Percent:%d \n", percent); - - uint16_t out = util_percent_to_16b(percent); - - printf(" Output:%d\n", out); - printf("Expected:%d\n", exp_out); - - if(out==exp_out) - { - printf("PASS\n\n"); - return 1; - } - else - { - printf("FAIL\n\n"); - return 0; - } -} - -int ut_util_percent_to_16b_test(void) -{ - printf("******************************************************\n"); - printf("uint16_t util_percent_to_16b(uint8_t percent) \n"); - - int test_res; - int pass = 1; - - uint8_t percent; - uint16_t exp_out; - - // 0% - percent = 0; - exp_out = 0; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - // 25% - percent = 25; - exp_out = 16383; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - // 50% - percent = 50; - exp_out = 0x7FFF; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - // 75% - percent = 75; - exp_out = 49151; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - // 100% - percent = 100; - exp_out = 65535; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - // 200% - percent = 200; - exp_out = 0xFFFF; - test_res = ut_util_percent_to_16b(percent, exp_out); - if(!test_res) pass = 0; - - return pass; -} - -static int ut_util_interpolate_1d_u16b(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis, uint16_t exp_out) -{ - printf(" Input: X:%d \n", x); - printf("X values, Y values\n"); - for(uint8_t i=0; i -#include - -int ut_util_invert_8b_test(void); -int ut_util_sat_add_8b_test(void); -int ut_util_sat_subtract_8b_test(void); -int ut_util_sat_util_sat_add_16b_test(void); -int ut_util_sat_subtract_16b_test(void); - -int ut_util_limit_u32b_to_u16b_test(void); -int ut_util_limit_s32b_to_u16b_test(void); - -int ut_util_convert_muldivoff_test(void); - -int ut_util_sat_mul_kilo_test(void); -int ut_util_sat_div_kilo_test(void); - -int ut_util_sat_ratio_16b_test(void); -int ut_util_percent_to_16b_test(void); - -int ut_util_interpolate_1d_u16b_test(void); -int ut_util_interpolate_2d_u16b_test(void); - -#endif /* UT_UTILS_H_ */ -- 2.49.1 From c50b3d90bf3e7275abb3736dd75b4ace63e5c400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Mon, 8 Apr 2024 19:40:48 +0300 Subject: [PATCH 02/35] board abstraction --- docs/uDCCD_Controller_R9V1_Schematic.PDF | Bin 0 -> 895144 bytes firmware/src/board/ain.cpp | 35 ++ firmware/src/board/ain.h | 37 ++ firmware/src/board/dio.cpp | 72 ++++ firmware/src/board/dio.h | 39 ++ firmware/src/board/halfbridge.cpp | 68 ++++ firmware/src/board/halfbridge.h | 37 ++ firmware/src/board/hvdin.cpp | 37 ++ firmware/src/board/hvdin.h | 34 ++ firmware/src/board/mcu/mcu_hal.h | 125 ++++++ firmware/src/board/mcu/mcu_hal_r8.cpp | 464 +++++++++++++++++++++++ firmware/src/board/od_com.cpp | 40 ++ firmware/src/board/od_com.h | 31 ++ firmware/src/board/odout.cpp | 44 +++ firmware/src/board/odout.h | 34 ++ firmware/src/hw/mcu/mcu_hal.h | 102 ----- firmware/src/hw/mcu/mcu_r8_hal.cpp | 463 ---------------------- firmware/src/main.cpp | 89 ++++- firmware/src/uDCCD.cppproj | 279 ++++++++------ firmware/src/utils/interpolate.cpp | 163 ++++++++ firmware/src/utils/interpolate.h | 23 ++ firmware/src/utils/utils.cpp | 137 +++++++ firmware/src/utils/utils.h | 38 ++ 23 files changed, 1705 insertions(+), 686 deletions(-) create mode 100644 docs/uDCCD_Controller_R9V1_Schematic.PDF create mode 100644 firmware/src/board/ain.cpp create mode 100644 firmware/src/board/ain.h create mode 100644 firmware/src/board/dio.cpp create mode 100644 firmware/src/board/dio.h create mode 100644 firmware/src/board/halfbridge.cpp create mode 100644 firmware/src/board/halfbridge.h create mode 100644 firmware/src/board/hvdin.cpp create mode 100644 firmware/src/board/hvdin.h create mode 100644 firmware/src/board/mcu/mcu_hal.h create mode 100644 firmware/src/board/mcu/mcu_hal_r8.cpp create mode 100644 firmware/src/board/od_com.cpp create mode 100644 firmware/src/board/od_com.h create mode 100644 firmware/src/board/odout.cpp create mode 100644 firmware/src/board/odout.h delete mode 100644 firmware/src/hw/mcu/mcu_hal.h delete mode 100644 firmware/src/hw/mcu/mcu_r8_hal.cpp create mode 100644 firmware/src/utils/interpolate.cpp create mode 100644 firmware/src/utils/interpolate.h create mode 100644 firmware/src/utils/utils.cpp create mode 100644 firmware/src/utils/utils.h diff --git a/docs/uDCCD_Controller_R9V1_Schematic.PDF b/docs/uDCCD_Controller_R9V1_Schematic.PDF new file mode 100644 index 0000000000000000000000000000000000000000..3d8c63f67fd87b496f0c4fdf222a39bb6f4e782d GIT binary patch literal 895144 zcmcG$bwHNO7B8&QUDDm%-3`*+-Q5iW(hbtxUDDkxjg*LVib!|g7u0jy`?$}y?>+nT z58o$dX2q;o>$iqy4Y90{2sJ$o6AUCV?7`eIF*c&?h}Uq)qyZ2U&_W&tlAi9zFWdKD2A1!?jE_11yl48+j`>GB<{#}?ezar#(T?p$JN6&# zv;d?8ZJewf@#q=;$QnRJtbnn9=oi44y^Vo_kt3c4EkHqdw2DTqj(Cr~gQN=MA~t|q zE-t{8q9Q#WfIRbe(Ezs$kGDT0d2IWu3V&ge_PYxI#SvgA0VzCMAw7EwBkMo*dSv9M z>HNOe4~}UK^zi8a*!Pj;zwQfYPe;SZ@F#6PGXKXusxnVz0^ku~Fpf`iF9J{!;9lOy z!N$qnz{ufI>L)D%9!lvs+MBuJY0%Tqvpznf!+X49VFX-IGte+HGq66*hJb(#pwZ)N zRyt-pCKlGmDUvj@HgPn?qi3cANJ1-OX6Xp9bXpNhz@!No835+%yL=9gfHeeQz%{L0 z?Xxu|E4(LamiOF1EgxD;kA>XJ;5{N!pHqD(&o2_I9HHxc_40kvAG|z+T3XAU^FrnK z5*O)BT(=Hw*)$*6Geh=+uEH5hTROICP;F@1y<5B*ZtdEdI<%b{GJBSL4z^IhoK`Q6 zM($32ZGx(cE}(}DZasJ9qL<3xjWL!+eE5tGJpfEulR^mM3D_zlA@ zPChXnDK%WG4)4$C(>69vY_{p=c9Y~;br#f`Xtq3G%zn!U1*%*cT4Bi%Kx@W!lBGC0 z$M`9jd{S{qCWl-Q1IY=GjG7(^D2IR|RT^L1M^AfAQ0gUWl zibRye8DVy1b6WKy2`>ho_0+&lEOrMMT5<*y%D!R0Kp1x`?W9yqF2Zh958;4BKi~_( zFtNyq*?E-_hcuz5AFi~KA(2NRcX!`0c+!w<;TwHU;0 z@X&cl2?G@e=ZFd2hYfG3(Gk)@M835ders#rT-)3|C}5o4`$R!F!9%FJPts3E+4@yy zJ-0eTEwV831&I_x1yLz^-INCv*CP0*>x{ZP03K{C748P=2jKRyNQ`Z zHd~OT?=TxwryS+_xIRjdLVy;cfuc1CpMAd7Yp9^PlXU*N6^EbE>+Q%)P#zkQ(6l{_ zK~J#J)`;0EQt$u?JVugycVw)FLg{%sF-^ZdUTqIV(uq8r9V+{~L^ZuUYaH_RB}Dp3 zsaT;nVLn%v9sdDpm2$tN{Y&}dc6clhS|pcS`x6iQQisdhp3&W|J&uF&wbRz9U42bw zr`Q*m2KMC-d=SS3nUJfpVg1a^$6G}+i~1wNJMHbc{!KZvr>C2Y4B!GYszdu_AR}g9 zL4>BMJk%@}c`8h(3VnG$w|Mtb7KU%UVk=4dsFpZG!_8MwPByqJs^9rg<`PMl1lU9ZIldMgmwBt`3y}vp(RR@XCa|ZQp}7E zaW@&D6AYN4&aM$rpzv_uhRVg{-Yvc87PBzJf;73*j@q zU42+|-hh^<%|$Mi$&~P(R$fr9_iaQTAD=&G(N?`xKAGx?6>P1^EK0vSKOMz?hU8P; zzoBC8fOUziwCeSCbwB@T<)z34DhEwcV&mGNi1yd>UWQJrH|SchvE|GtuAn%=^v%Aa z8Wv@j2nQK&+)c^0lWH^_5p7DOLwS^|nY$518ygc*hO15^9bMV`AsVc^sb&u)2;o*mG#}kUTNK%(v_QrRdNRq ztXLOg`+4XfF&^u{bwg%$l3;x$3N^$qgq&dF+g!)f@fm$sjJ3OP{BN`;pE?4{G7z4y z$4^iH6NUid%r61)&$0f$M)}78^yeu5gf;#U<)1?NW7Ni@{So)RM|v?4I=sJ#>yHEf zF=GCmxc)07{J)Lsk4*kG#oxzufaZRM13w}y1M}aDvy>Z~6p9lUj*POlgDT(~n=c5S zk>3Cf|9+1CgS$tl2%sbT9Y{SwNdOR}6}7i?!_kiTn%skr1KL8{d=;_!1BmE5^S*P)DASv3whqgp3 zEQePgTPwWSTt6Sw4pIG$?d`gvv-RCItO`-0W&~h8gy`fA+Mkak_pH9HeMpy9J=t{> z2$35t+}XI=ozfZGuSn=vt~l5#-P-DU=y*_D?i7r8XmV{^ubPD^?EYqZI_B|qp2_7N zh*SjQb6EkB;A%|{3ggv)iXGN#SH^Tr?@VoxCUd!qrSy)X0|+;@NRV3{>n1SoaHKJG zSGgFp;c^#jD@nw{c-(Sq;!w`OEQyQ5pR{f9 zKP+B^M?hn)7raPSjFdHE{SqjE7>b}5H*zUnAb7c!f=HS-g3h@XpY%d%@xsqk{H=Hn zsINR0!3SR>A>t`~A+hX`^+SA8aYcPdA*W@#9CQ45AsFD93S~EbPJc2OW`5ai$Q(oB zf^tX?csU4pv1vZpTD=j(;UY>3huVnAMxwW@i)0dM)6d7uh(*~;-Z8CL1|n_}RlRJ) z1%jUCc-|91<|RvH z;Zla_1sitwgvZf@$K9(>VJf^<9r!lm$`C&G5MB1H^eJdthBwTw zxbFHUXznx9DA06bMC?*5%Bs%6OrL48vUAf$6RjaOqf_BKULsB*n`;{Ico&7Ji;1Rp;8nt3Keg=D5okIK)b z>?7;@YETk=fjgmo7o%e(47`A<^^NMlO#cDZl@pg3H1Zgs@qz{+sDQ(iC4-Jww?xaf6DPAvBJS zso?M&VnUdxUp=wb4Mfuczx#M72jyC)`v$%~V7|0kXx5m|=1uydb6fNXC)Lo4)4uC_ z{q|7m1Z7ZiCUv4bov%Zef!)}5Qk=!U@H7iW#gi%-(DApo8;#ccT{N9%7@;UQ%a@$@ zK&fsfL1Bpy;6kS;Hy!jxuv{r|St|RiOrZ#)wdO8jaR>wW>mLI5q`p^dJc+lAZ0g3^YYUsGJ!igf3`%p3ri*m(s7x17*sW9|s&-5t%PwnD1MnYFUxs z0l9TfgcN8CB$4YcRsg-3bW*85IvF}+fnb2|Dyc+olXUSdv|*Lb+w(a-if!Z=eaA_c zM;it3#8(Iy$^!p-QL?JGBSWQA^Bm?{%7BK}`fbe9eg8E=#K6Lo8rh!2U1Lsk(SuCW z<89XWS(}}y1+KLq>dMf@>UP~PBY+Aa%O=IIJYq-GYWFxlECWFqW4ly_qgLir&7!}5 zi=m+GXNct#m*D>vH$UQY{p#N7_7IaiEUo>fm;;0IF>!c6f>gYPF2gLx6vjyXOI3!* z7LGh%<@5V0^O+-2NBq}}y=CMyl@wa4`*3cKc6ACn-L(-X1#R5&PQ|EvC4t2sb{TxD zSTw}px6ux;`@q)uKOs)Mx3}uX$fER%gFc_DPU{|YMWZQ(P+~gGEkHyK3N3VGKZrS^ z)9k+BIr`eIyrvJ@46Sl6F3f-v7f$^b~zwvkKuVo<2C(7H`87IZjx>*$&a+a`oM*?0RUCmNE^{9@x9mcoRN z6)vQb=xMcw`v{d=&q=Bx{abkl&%L+JN@Ereu{;HU*>y{OOHP3L!Rm{m?ON@j}b<1I8s?Xjv=L$brBzA!B}e&r!XVTR?iUmja~yx=LP_DTj9ttocN$`!@oJ zC|oVN%;2vfjL|mI;!8(7T09em`du4SR_U%?w0t!H*@dDg(S=d3P-EuKT_n28hc}M~ zYYw5uQGriYd}-*oN9nk+*_`O=SsPTVlC;NB>0RD0+UG!+1vguhpL3*Cz596NEhc+= z#&2>?4R7__wZU7O^Z^L}h7;)b7#Ogx_HP*cXMp)%^A!3geEer<`LrAQM`-z!&^%(p z-=r!}=nDX%{sTV#0(*Z0<^B>r{vym@bo(#x@xLcXzlVz+#QrYI*(;+;(6%UPo;OuDDvp_0EHRxIPkHUs+sjh~m9oM;-6D{kjKN z(cyKozq)yJxK4Z2G2p#)2M&k5#=GoUbu_0h64h^m#vu&ld>j;XDG0Z8ePw%lNyyu6 z!#ale5|>w>|$x8?F*69|#lr~XM9JILbnS`-TqJ$(F zo!7U~n3ivV5!AiMU^VHmxrpdaRzZ^P*27S%-nL!d4B&DrJUF@@Wwy?F-meif&3xFg zDF9tBs6ALQ){wW_FZ<_4(cv8m{*d&>K?V^b~Ag-d8J_z)nffz8!erB-FlO!1#3{ zhYxt*8wJSza}pI*A0IGgNWHI@&oEtGoryqc;Qc;+?w0gZ>7wy|$6F5PhnxMe%6MXm z0@}N;f_z)(4z7yGr-W9!uYbR0Jfq@jO7O zA6(>x)k9;n++da!z}C2#Eh>ntyko@g9mH{3#$zD8k7tznifl()XQO%BqRl(yD7YFD zW5otyOxP2rp>69#KRFDQm4Ok5Q0Kd|$SSXR8!^W2aENDJ8mjDyE`p9b9Xp4zB*?d; zj3yogfbGjERI%})^17MGD`|xtH56xV(q4_1ugOsFP!?n5iv-uPdM-up-ir+L}Dj-HY0?CgjoY;QlQ7nYi*{bVY!VB+074h_=`7Culue%Udg*7%42HJ|3PLL0u@u_t znw0^~1?%9G{B$`a4bT#z%)l@>qLNrC+Lg@7dMw66fsMgu^9*`)&mo0bqAT^=>Sv4J zgZY@37P|%Zn<#?8tnaCn_Mzyf*QPnclk}Ti8irL$Yiv}<3H!cN%J?A8s6{!OB3uE= zctsUN=}7Mp5tLS#n}Dq@P<`B({Ob|oB^bH9Vp*s{ z!EpzTu?*Z{Ak+y%tKRENbd8L*Ri2T=SPprbhT7KN z+!=!eZP#!Fz9l*-AQLo@n7v8@Dl`pE$kIVGcQ^^bm5_NGKWJT1wx!m8ptx&z#)95{vx^4{WB*QS$DXz{&$f3Y9AV>C+XWLyj^i;`}c9t?WW$dl< zcm=IEuMZhzpu{CdIPTlzRjD>7mX-x#2>q~xNG3{1DQ3IDDWuil4TN%thp4#|Uh*r% z+g4N>aqhY07+!bc#pIz#A-0}}t#JAj$7NGY<*@kr=f97)a2_sDg_cR}L+aKAQsUPz z?KRnhgm+I7>zUA17g0{}KoHnz)J*1V0XFDL&gx4m#7lTqE%RQS@S_um20Z<0v>&|2 zF1wHk0sT6@P~a`eeK6H5-N6q1G-)!0(KoU#__q0dJSww7Z$^W0+{r0g zv0nlOg2(()jOEf@v_O7v^?hBcxeBCVKG)1u!bY*sOGlK~wK}2szHajrm3D5Xj#DVX zY7KVEt{W7Sm!To5`chbiu4M1}mg~GAkoHiwrt?!T{ z22sUhIKNyA{U!(+BTyF_b6=NeF7t!TbTM<8(<7PSe0#Tfs)|!c6a^x9LDE8f#9>Mj zM?&oYE(K(?;iwwFbSzxtystv%5PI2aPh<$e$TcVrNC}d`q;^rWeOBiW(6^2zMs2hv3iJ$^bYL!g!)97m;+>WI2l=YEZG{#;l zrWvoPddT`zf%6N2?qJL8f?<|z187AE{SZlCkiYl+PH$!+%#eqB_$j_ZemFL9lm5+H zA0j#G+%LZK3U#a?_3{N@!^5tp8D{07tmkP5-#}>S-@J+Y`kp>69xeJ*D9r=>L{f%WZ9+a@Tl6_iF^_F{ zTyY;#0PUiJhineTM$u3$l~lPdvHf29T@Gb@|CBPhRGnYL2(UhwAzn%5i*ZD*gEe<8 z9K!eKdy@<9$ZNXsHWLVp)q;yK@7h$t*c`e%p|b}x>%7l}91aR@!%D-s*XsA^V^ z1`$8XvjLFD_eq`-fIQ$&@|c_Y2Xq5Wp;l3a2-@Kteq}?3jI=rU?wMs+iJGqjEshHd z8IR;cJB;0RBQ80q^vq=vAizR2fVR}e#~RV;Q9ei@CeT=rKAizLfQ!bOoVE1@O`MlUSl zQDO}LS~IWe`$<;%OTZuxzT_OFC4aB~C)vnpsosg_A-pFFL!8_q*KdpE`z-?PlcCK2 zYW)w$z9Spd&z8y#1J_?N8=~vu+KtmgK}S|)5v7upj&>4Asz!d)ehs@mA}fL*7`*3J zL7bg?XfM7*e&ELu1e5EJkTVqG*B`Y@xXItfA1iKa$QRC5&Z-^C4Pe>H1@|4N6r2db z*GPx4n&H5s+Enj!BW?^kb!!8ugAn^y_$KH=5s-`|>`tao91DOEosY9H> zXD=-R#1V|{ELdmWy?ih3R&qi;s1QQ-h3c}%ENFX^+J`&!^SLw6`FwM0SIx)y&Ga%A zjUMj0Wad)^7ioIE1ciF5hGDP;G(dnYSxbtK)V7#$4z(oQ6t*xEbZ_-3t1q7ck00g- zv6Ut6Hc$0603NR&#}rAuGm{=}HN=6QG<3SokM2j>sKFRBA6VOD zm$YW24IcZhEFKed^g-t;xg2_#(b<&FFA>b8!_1|JzRT>ygaaDkaAw>56j^*k$7k#3 zCqh>!vJx@v8$i{3>=@JmRhI!TidB`yVvEdV$cK8Pj~E>0)CGMKw-;F2*fx(><{R}9 zUYO6*q(`9#Q~C;plk|u1X7U!c77o0^Wl$QLqwg%9<1xe&QyeC1 z&N^4zlkzE#E9O|w;i(I+oAymTm#9``lhFup#sR~2Xn3x_WWp>tdj#@rT9+Q3k`5)C zUAqzGaw)>aKC>*^m*;qWHtAtqjnZmDVTN zx_9p+OQs58G^3mkhvunmsTt@U)<;b6&8;g~z<24P6FewEz8do27r&l*{3bY`Q1Cr3XKsL7MWjJ= zwq|F>eyL%KC_IrjaW<9rUjAeh@)QT7le4fA#gVxW@%FcdPA5CA!2qVVw-Gdm!Lhs3 zNTdaGAji;3x+q@xwR7mCH~y7_H&4Dm?NS)&G6YTfnDpL~9Pfv}BwuG`Tw%5>dRm z6&^1r9~;fIYzM~nxn)(KVKz0Gq43u>h8Wh_cm9?2r9VWQeuGe@h!^dxY)yY4K*?{V zM{Y1~+h&+Lp7d5IKD-*r91!DP&WS(9xE+OIvRA*vxG9PJPh{qLrmzH6eeA(h3dBiL zT=}(ZFwx(Ty45Scd%0dDX8jN}7RL}?3%DOaG@e15@hGk-E1@Vs&XIqwcs})o2DyS$ zzp$q{xto4G zctltr0E89O`h>8IHK#>menD73CbUw0sXi7^vHjN})RAX3lJR(%i`jWh3#2CyVxwX( zk08X-8SHaquZ2|z+RZdZb=s6c9;_OLaI3L%5NDQh5u)5|hqJ<-xKCMJA;)qKSAKBa zSWMJ4iGi9Uot91!8Y)`WQGK>*R)w$oP10%N;j zoTT*dwaCO?D3`uzkLc>q=DVb%!vv7)-UKVNVE@9)8*L2?bH6K%{ThfomZYY2fD0ms zt7pnnSvFf2vZC`I_GIyy20ck}FDxgi^+~ZomRJE6_T^b~ifSkWt>K=&aF;Lwv`TkI zc&Cu5N$bBK7uBuF66Iduq6lzqwPrJIcW^1MEb7l^Sr*uL*SGTK%v;#3T-qBR)J)sv z&epWMF^l_JTGrpG7uC(S+^*-MX1YvV?VMdzosrCi-JscwKsZK>_4^IIjdx>dVHX>B zD6G{6e(i>|8KIUe8P{9qK}@`2ZPvzqd7O3_;dVr0uAk5<$uqaung_fA`lTpz~ zPkVyJY~}*RJ>E9y%6cTtK zzs5<-V?*c5nbo*&y+h@mP)p(&o5DI@G|0M`wY=(NlgqW2=5f{w%$GKG-iy2Q>`S9@ z9^6-KP1J;(Vb%zwRzA|#{IL1af$LN6czHV1e)lj(;$CYdus~vFAh72$xX<%x)6=VA zgYkRhj>#@wYkBs2OEfST%)U46md_C;4QG2)jnkno?5f}Cxu&)i-4N3;h3gzOpAjSP zeqO4+^UTx@gzspVUhY-*6%iDkMa^q_rrjFAq90K#o>VXv+UHgPoU z^+^KH7h~~GJn#|`c`ei_=|F`^dA6Z#LtrpC;$xhoHWk|C*=O30PWvvNnDaBKK7GMa zym|!lP+wTI=T8Oiy! zFdGVG93p;zjs`!=H8*d*d-4XT@w>#f?)|jXV3ZJ#@#GSCdTR#;g;bE-?uX&0c z5MkneV>sVH4GOw8=*nq@qYd+8iVJR*bjn56fk1o=1wq-U?N%EbC^yxczRsIEoe3D= z_dP*Y?edmyQ)m-fc2j7X`<3ce68J^ALJ*;_dOzO3J~zi2AISP-@SJ1bc1a-kWDIlV zldkB_gRJ2@${X|W#h?hx=X+UPM4GMlCWBq_rg&cV*Pjw6U9Q>_#3%a)u0uhoL)HuR z>bGT|3;REFhrQfv5!i)kLJT~AXa8kArfDYID{{5w95lFV55H|IdEcPyk>~m za6Z_V-nPy)T>>|GY*adcW{Ca5CtaQT**CVCQ9p&LUZu3@l{E&vf;Gb+@_n!Oz)1YH zuJH=Esbl5RPR~Q%&#(4p&t4-R@p@jcN7h{?zI(8ba-QbL2;bciccz&_jVmY_l1p(O}3yCzI^zhMT$MysVU z)Fbey=0v=+b$sXamBT7}AV+>Ve)QF9R?_$@C8#z|Sp95G;wcfHSUjpP5^&lGYF=y{ z4IE@))SD{;$Ko1TU52kdsFsf^pC`eS+Z)K72L<8AOpIo%KF>Lgzkx$qA$6kja)0Z% zfJh+5a@82F#3VsQvU{<*j?_F8gmh}=3>mfEvx~B9ThbrS$TY6-{@@;xMjF>?M98Y# zYtB`rIiNw(ugbYi&l1Eb9M*4XIkM#j1sra#XoVU+g3L)kG|sx^|GnE4MaFKE6dPL0)87_)tH7Enm8k@KhDeU?3*&WvcMlf)D0=Yum}1 zFSPla5yRY|=&9a#1?7k5yUZ?d;8TY5sX{!)b3(9OP#h8O5VRtj^$*H9QJwnGjM6gE zY=*^?hp(qPmp`V0h7$M+%E?_%wM%5&I-2lqvI@xNAKNJq)$-2v2AYIs5@?)9xinE{ zNIK-szmS__qsJcT7Q1El>K#v7ut7#cP93G|Pv%ZQ5wSi3x3A8GFYx}c&k(!timD0t z%3)mY=QczA44v5BRJ%%srjxgdlX5l9cwcT=mEXcMxle;IJmHKA-l1A$6c_`L;W2J2 zU+~Pu&DwCb(0Hn1_`x0c{yl2Aroe5}HXt>opMKoDs8?e6^Qbik>r7TYMP-b1N#LQy z2ML^ETMG`zXvBWOE)12JzEaaE5?$9%0kBGY@Deul~ znN%jSxgnQ@LY3$8tHVe8iZmDvysu_6x@FrwZ4s!VC`%MoWU=YG)0S?!HrrhseLy$< zbRWJa6YQ@rkm!$rCbySz!lx97VAV>PEK8MRP76H~xvqA`yylCP^s1{qhYo~&>Xpg- zSc}^~SVRr)6fu>a5*3^t^3hdBSXReJjt;2fofq_g#4`h=ft_-urBTy&C8wQt$qy+{-ern}6)A<8W68jl2Hh0r@zc}i#O z-}Z`VZ@}q~F9xE^$Yiq&%9878fW*e}?s;qsN?kZj%}z*Qg4GRqU_T)DSB_aZy1}$C zGv0KK){J)1!BTk5M>Ojoon{rl%hO7M!*PReWP?exh&%^uIK1)`g$Mq=Z`Q0=D^fVo z)61@Fii32Ayy64K9rsd@Gd|E0pCIwRb~lEUck#1K`b_Dt)m-)<-e4%VO$-j_mk}%E z_qmaAC_vAYso+0>4DZ*YFE?VETaI%t?gX+%l6S0g}b3^7l32aMi z0CzHUOo~NRDsbT_$$}d4djXiO?YfU9M$Zvw+v3&rk#2#JXcq2zX85fcFJ(l(izRW?kJZ4kzMC$}dWa87O!9WFQo(@h z1Jb${C!hPX5#2Qo%okzGAoZ^{L1oR2Aw0P**c?7n3cN(o_`W%X4({b+*X!e&yn=VC z3W`GMA%J3G(LQA$M#rQiHm6zxyeZ>><7PkAwpgpwBvQ0&cQ)7WJvy;c7GFAGafGpX z=}C+rIb?1MA14KC$D!T+)pDy2QRDi|4b6l(-~Y&u^2of#5Hnix_9+D#fEFr_R;BvQ#nVxR5)Ft7&J+`fHiGmUh@Or>gX-1fBv*FD1C@ zGhQ>aSCMYdbo7V!>7alnLP*ne!_8eo_=u3cy9|0DIrEM91)pz>6?#u9rg)a`AJGB< z@-H1vt2H&ZqVRTBr7^~J=2^BnnJ7}dxp1yLI=9~ksFj6P1_)Y0?b(4~t7aJ3VX)!a zPxBLxXpP}~>$$}|!soX=M3oV`V3lLr}L?|cgCih zg&1%@BvVJmq47=*$-gUq@N3ne2tDy|hT|xrWS=i3W1vPAU$7{X^IuH8 z54| zKthiL83rjP=YBlNDWk*dX8vhIFVWqyF@w2IkT+9t2-@CD7VEBWWa8bLeZ-g1Zmvk= zaQW=a4l~X_gjXE8%^Z8YTRb*mLfj8mQXRLOEN6_3K;!w-EzAi)-}!t?9w+-;8puqgo7%&?1-TO8cqQN$a-;;(&x|W}j9x|O zX$q+kI;0B!4HIR7n;7dNMCezf+ zBuh1y1)DJXq>q&N!&$25epZjCYnc7iAJ*r$-?B$-snorIA(VC9>ON~`C6&ZF>N)!r z<7H`l1TmfU=GQ%+691+;X=|nK)*+qh1kq_EvV!posS4i3FS;)|)5n7ZJ(!db&aMX| z49V^gM~W}+uo5+T4%nDd-hj&Oen4T7OoV~bi%XWksUHrkIYL?sU^s>7WWk)O&B)m% zDWE}GS)#+xnLE^C@$KF7r1<*rBZB!9f96Fw3i$>wd+RaN8}SQ-uw)(Vtd0 zpvl#4%QC5AyB?H6Vj@B4NzCf?O7FacX;Q_}>Xu*@OPtWt=;MwaOD=8rm-b_hCOyqS zm!|0U7rWUe7s`nM8sD(8wi@M;v^=AYcDIVZs(ZKI^6XAYaWxh@dbzZNvuGAApR$8R zPmGa*py5Yf&l=t5(2)+lA`!oq70SWWgQ?{p)^l`Th`xN)$_hLPH<;;Pn+*HSKzXN+ zpmDfBA*gq&8Vn6R`aBR8lhaQo2KgQMLqC2rd>4vwdJ3IDVp|tP$aIBDK$&3)U)0v# z)s1n8KR65Bu0jA(^EU+PS9xB36sE)qeWk8NM6{I*bDd=h$Ref#gyC;rwViR2lahND z1VPc^q?{N_f?~2ceeD9@yYxjsEVhY9yz2IkH3W3MW~(Z(MQtEU9=72UpAbxiS# zp1@D3Ici8dj3LRS&Mv?8I*J#S(=JXXN4+#)CwuWCn7~3<%ij$QXf>5 zGq^=m{ZJO73sfVmgqDpRMJGUThN;L;sfmn63#Y!@YKQbFAYi~m?(TmHIHJJCXYid9 z8D!}%)uL?p(alBTO<~`Nh{t+$adTx3P`W?`()9s+MCtvr&}3lC6E2X zD-HcL${~zAU@)LSapc;VVtsrxTjMdY;Q!z#nc^E%=Xaed1|ZBIffEwRckn-U$)OI1 z41L}V4bb+#-W=Fy(A$v&PQq{Pqyh$a}@1r!;E#z-XE*$54Dy(a_0G_^e7C!?&qJ_iHjytS5ainiJ^3Kpj#fkP)Fl%#j*3*?N zq_0C;OOZ#HKdfF-Eldlbo#lvE<&ph-o6@2LOKVu6@8W*RJ~8GYp{v1D4_n#7g*?C& zG3NRTRve_&7T_=BD46S46h4c>ixTIjOlalZ_kbRRL$KzxUfxP7EH8XQIldr3E_^Gz zQ(A?rdX3=X)bUc#zs zdAtm?XZ>vEt#%QE01{wz?EH8YjR2VnWay%H;TsLli(@klpc$aQ*0T~1b(LeC3o|d! z#~EefeRCR%`Z0NsBf{qXdTktVbqzyzV)HS1RcioMhqt5Fc;A2z^Fh-)CVI|o#Qk0K zj1g&=)3(S#>NYCsMnlB-Q3hUJLbgqY&ca=l6|prJRMH2VjEfn&>TVB|1i7WgmJTf= z4hx>fHr|K5t3;iP8LXU^ndf&qE#nXN>4RlHC8p;}(m8`Sbun8`BsC+$WYcV1&v6*nS&YG8JC9w)C}jGTfHZTL{x z-X4clR8+^Dsi3BAO0W-gr$|%oI9o66*|~V0pN}3Lo_t=`?%=#V-W}(~eXvV+Tf$b} z^1j*|y;0$9W508qqI6YJYg=2~z36D?)G|qO%2?QM2qWWd_qf^J2`Ys)$)&fxx=PjY z;%UDHn?8B4bG_NU$@KR0PQSCv3lu5Dj5y9t6;h92ZGUso?v~hs5|vtZ5;|P&eYL(b zdc$fXvt8|#zBd|qx%heNriYfd!|SeRb#%x4s|NNLmkOQsmc?u3jv%kL52YGfC@BpM zpT>idO&!}mkR4F5?R4C~zPvDbI6ioAyo}OW?g(OVX#1wNJF0VJW8?k$#N5i&#?;{W zh~n#UPijz*#w(kCEm|4g+kuwEhqLR$;Z$51+Ng$B14`gKrum0WSlYSJ;db|%p52kE zAYL3EAD6bfql5-4Vb$e!&xdPZnDL(7ixsoi%V9Ph9jiuMibG%LKQBwm+CJDu`3@T- zmSIPrcn^kt>m&RG*fw)rZH8q6y6vP}zkQZFd>-qo)NOfTRanQd+E3+?NKc;GR{y#+ zX^7K0ta3@SAiaVraZ=M$h31p`v(1=t5XVnz!&HXhv^A${2D>VQ7#ba4Yx)yFW0~uj z=1d~Wn@KDzXX?f=G(Jhs&J8(dGS_?HsTs6@U6)m1V+>+^&i-H$VdW(@JGYg{V_~Ty zSKWV(?4-8t(eNH)xn;&AVlPT!cJ96r@D5HN@DA@>&ESR$F!*@QIL2p*)Uv9!TF==z zuT8^Y3rkj40GV|jH3QE58xBh668y8EZ;FDMb&l3V=n~r^*OpZ^Y=dZ= zVN)g%*v{g!b9$s^!k*!u-w*IpqPlRVH0IndqE1@q$61^*n8aaFxKf83gq9luqc~k- zy`taEoxFEP!S}MXA}1K?4|}879SPx~2wN7dboA1^H6V?(8kh$(%(d3uf8@BIUCtna z(I;w9U?8l1v`MxXsr`r$EEt>pJPAwp4F`IvgKAZ{;L^}yp=6fTEIi3YO(305)DS^0 zwP5H+*lyQ7S0PyA@P^=V5(`rBk@=7TT_5DGB0Vl|HXi9v)pce)(Lio<&Mx0MwvZQM zRw%uQmqk=%=(SD~Cb=ZR13e5CmA;~&JfKjuqnrjHUMjii)niGW17yg(0-yNDx1-ZT zgEpO7-#+Ui1j3CMh9rIo%sSSuQ6o>rKSyXgv2M@EhZ9ap$Go79UX+)+J=ezec4yj} z6tZ~Hc<#=L54M#ys-`A3_2fB{Pn0YtA{5R{)=f=i;81UFwJrjVvOiUK?)*^_6$x&~ z>_&BhZ)aJ)sS(0M&r1JjsM-2{s+>u@FCR~}OoTblLdX4#G#rl?A)W`b?@&{h^!8xV zsX3^&yHgb}^206wS;UXDU`4h_3>YymN~_#&u1DST?yJNc{R^}u^9Ab5Nv|9EnY2H<;TJO(Df z2jI-?cz^=q#~1u~>^};0|4_F|_n(yq{~KjGRy;;PaW6d+BOW6o^LNUClXAWn7yps; zuayMtGXq$}KRefeqmdYy0Sael!eeClsc`z=nb~iu z!vANf=~)=Q&o%2$3;(sK=wEaFo8s{Qjr8NXW&|kwaa}Vp{8aew%@Ad-7^Or2h2~fPa(zht2H! z;$r*m&FENulK!2U{id+yf0CO1(ZX2(-t5uBS$}%7?`27U@@9ZjqyE{&^*Gne-`6$3 z(tk?*J2L}>xPP8BAVxm9lz%JycixQg9}b#Nkqh9>o+8)3ycy%~f+piXY-W#(>oI6P zN&T;&$@n`n1FZgkR^g9k#`+jS9?k4$`1pAs-LF0zP{;O*o&7)5**&?WKc6!5eHs6u zc=P)q7muWWsk0LSEJDU#t^YTLoj;2SAG`j_`EPvTp9|)GTW9w;^?wYPfA02Q>g@j0 zI)Cl_7j<@zpzYr@e-w5yv$8y@;{W@=xAc#kHlO*RpRH)Ux$lYCTY(z%h_b@Y9fK$J zNy(hZ=99}a@b)BMWM-z4N8At&#Kk3RCu%!eOl{)K?8Nd;-fq9)I!$NnIcPs&kqMG40HBrVYnO`MIXtu~7$x8YN{h*+@u ziAXF{G|V8EV!{F5G@D)t*MQ*${{E{CeX?D)ga@|R%*bs~Vok7m;A3};$^xGou~&lm zo4$H$7BFn;FbR0W$-3(A7M(GAoB*YHiI|9L2Q#e%fe9b6D%z%o?`mQ`mg`b9WW|ie zx4Wa-NMG%iYTt}*DEZt++s7?9lbg0FwgqUX=;p6cUZv4Qtt&PWT_8riYz7nBp^$xbn--L>pNmA8AWBrkok=Hj-pf85ghxkN~1_-}KfUr&_!5zGJK0^p}ZfB^RWhXUXy zfBzVF{_4OY+Naq1bhhSG+6KTNfZajB+IhNobk9#0KMHT@0l4YMlgD`V`09Hl`QydU z#!oo$>DAB1-yIg9&Cg?Z=)ac=KR)?6@{><|?DZ50A1|Kb($mGyk)M1v;0ZvWU)>VG zpZyX&{~sLccS?UMT>c(4{@_rb!qxwDJm+s>?XTYKuS(;ec=>ay#Hy>S$6LAG;#=YcK5Sxz#$L`F;U`+yh@Y%8Mw@*yI?I(_Bo~6sV zJhe5&m48U0on<5QkFqaF*v@ z6m=Bo09r@(34NGa8JmuTvk*C9_T9ltmom`4m_%_a;qijI;yRu3W`XXwF>_kV2#gss z3(1vnLLYgfsT2`z{)nq@_g9QFVqhTf$Gqh)5Wa8%f%vzz1etrk_Kp&Fei+yu=h1t3 z?ec(H;>_EUNjwI{aVOQtZ)&h$nI%Q>@oXq^x|~>yY!)JyXjR2O@-S?--9bQns7mHi zB!w$=aye-3rPvU9T?|=YHmN#YcM&+YUfj!O2P`jHk&p-`HQ2rs#r@0PfzRS5Gg`T*5z%RWBJOu2KfH&onRuPk1J5XB)3loGyy^}&--@tyY@L*kK2PizRU*x zm9LTo{LNxrjg6T3Ts;{$uh)Eq_L&Fzv$1(wi%dbR7ea^bBrjkVNOm``K#?m$$yHu& z>}KuInL)NOAU$k!Yi=t*;66Gs9JpnD3f;+%uhx`(>f4%!b=wYT&#Z&M5w(nR^p3@DR@5l$tN6zPAV0kehkl%h=1`n=55?FX z^0zM-%UbNVwq8J47xyA}kAz_-rmJcU-sds0u181{X*DpuPI_ zf0%P%A41KG)}pAcQ*6jMl~NgCAB7P~YDbLgj3U=gnnd@{x=d8dbY5oNjthOo4Vd-d z{lJT7^zeB)qNZAZHNO$Q5HkhF@I;Pjp1t~2j@Hj`#amr|`6Q%bdDRf1ty^2$&XgrZ z_5%l8Go6Ld=^V?NLG51hU1WsBA|E)htoSmjDMP)i1&#`>U^Q^hiEUxrID&jw`gB)_T&_qARAj=RU@=#?|7;eVV5QNI`T8^C=Ih}OT`AGGJFQbfV zrHq!H%?uA5M(y&17VElHOYjSLTxYs3%pAK$NQIwAkoxq@_sJy39!ii) zEUPhfFjVcn&eX%;N|0`W5{AK(09CD8w5?ZANvTIV7X7NiD1^c}U!GXo-H(OMcH&6w z9wDPAoFwJhpf8zP*mhP&uIt=Q=2^x8wjY;iGX=o_lx;KB|DnE!N*K28w!2?yz7XWx zjaC}IbM3`KJ~fK#VD-sD-k*OM12UbTTV$qnxm0(fXL960$a2h)Kg;AgVUdd|UvgC4 z1l3O$qthhHq&^X_V34dZVx@4ZR;Mx!jAmhFbvCW{Z}$C4e0pq@q~ApV_=DFt(7aacq{Ma{JDut9@GM zI05aJt_BJotp+`pc+86}}MtV9ICBkh-q{NVf1waiD`oIjmA zy!c6;sK*ZA2^|UW>~A8e(?%d1C0ARjXc6%K;P;Zny5g)yZ$?~L?7$8iVhCwaU7+rQ zIi%rlG2|4LkX=&4%;a7Jhag*8^7pdFCLIZ0OquW;<+5xNPlRj9v1r%3!4vF|M8^o~ z;?mwDxH)rSLU1))u6__@rI*|A&o(dJkfIl<;M z>|j~ZIm#J4q)vKC-GrnnS`0ee*gGuG=wgX{lG{|UZJ*}En`b-6;3tDaDI#25N(=U9 z%F~9vi+X1jaOD}gRXsC+#FNpUz+{2k_h5Dw@4SeCHM-+7$P;)PMGp3#IRLk&;!-Wo zPYrfG%9oBFUWjs>b&ckH!|d{ZGAKZm;-KI4A+2ybx1)-cEumA~t+yn-F}5(k7G{ch zm~nwKVa*1$dx=^!@>8o9c4;c~@;6mya1dM zks;+{`PBH?V7n=Qgx=n05rZIwk6GaR2amaT!z3^mv6%x>^e`oUzP%now(QsJuPDTg zC`fUq*a`EDa#YSgs_p6tCbq}4k#-K8g049}p+0O{rrFu{JE_HYI?K}+S8G6)iI6bW zNy8zYJ_ezD^ttn9tum&I_ zD^bTTAx1`CBvRm%&Td*}O;1LP-@Yx=vZab~BwACi#v*pLqkFfQ_!2bLi%nJcpptM< zAU~v~WmQP>q(;RwL=sVj7FQ@?`-DCNnzwcwk7|**(b$x7f`zx9XA9<-%rJg$Z~BfH ztXy(^LWTZ)-!&5DbSA$L1NDQJsc#`9_c9cgs+3Sjjd?`mkDW|`s2`wsHX?ACS74&G zn0st{PW>oF?_l(%W}17rouznB4sot#?aMAlDpjBegG4|^vlaZ1=Iu4Qy}Irrf@83r zw^3CU;2OFGn5Oj|*+_q0VLUhlUB7xJr8kriNl=O0fu`2=dXc!jzbfWRNcjUOW%)F0 z&yod>A3>mw{^}5Yuc*=ap@f~?(fNS)x+Qg4?pT@%{yW+4J!;fFNxm8-l2Ht#eY@Nu z>v)?&Bms8O+!0pinNit@HV>r|uP4pi>d4*B>g>heu_VfSD`9(RtbvreCw1Rv`?q98 z49|3Cs>+u7y3fGIcysZWXx=A9%*s066fBmF&JK1gm640j1oSFxZPoV|d`=C`>VR>B zyT0Qamz{d95x4Rbtrr8D8$ij7Ry(geH_!7{RqQ59L`X46kAB^+=Id4lj@F@+B@1@$ z=2Uj-yerkFDxgEQbZtV7*a+&aL&%}%vyCH-at9a!T&AX+5s*!5oX$2PGseKD<66E+ zYG&GHFpT#{Rl!1M{CHk53G(c49x&4hn7~u0qarnW{zDYzDrmLwlO3qdR}O>B%`<6* z(5rCO{wHGJuo)zuZhMcV%6+@hlO&rOnwh_!bubBC*#YjMT9gPL!((U3>p^e=GAE;eA2ihP0+R_B>&Mo zQ=#pc);tO^kW(>r{3p@FXfX%cqIIkSjdKMb|BpiNuXNyZ`8YlnoLw+=GwUk<=p&Ae z3zSxio1kdA_0wFmQ(L591GlXm(Bf=316M7Zz&jHpwr+4E7sj_R?2EEk^;Jp4Ze3H- z3PRt$>DKi^1bnSDyXMNLdUS+Gv+X8}a+uRN6K@(L3BJ9!zEL%KNzL;| z9IwOOqvV;+g~J^wZ|6rGc^rOC*du559QbtQS7Ne9%yJ zD~y>9f<|>qB&td59mbh88P)FCXKWZ$@3Q(wxeKl7od$J69>|X#WBQ4cOmoycU4Vyj zi7ZvZJ3AOba`A(U;(p5HgB-Amyh1p()N*cdw;vI3jrzNT8*sQvdDHqq1u3I>gl~EF zrjb-g{l)gh1Cs;1<2Mv;m;#0<#l~Hx-SK9C=;TGR0;&maQAmqPMxPklM$G)M&nxHw z1x&I6mrXhwP$~g_swOZKV5d!(uBbDz?QFSGr%yI6)g$FY$8C}dvSts*H%U3N(a4+d z8jMBpEpHc@Lex3!srfuKZ#Q3XT`G&?UWg&qfwIVvXL1d7yu~5JPM)!VE zMxw-A9fP?KQiD(6d6=o|m+uRDNR1lbHWd=|NMjan+%olLswv9w9HkmmFmu<&C{@rJoT#q^(ZWc=S?gi!2G=vCmlelpLB9w7K^>Ot zeB43~67DrZc>zm8T*|yk>+24IIcY_%7?t&`|M!^KcBz!PO4P~ZP*`hwCp1&A7QE%! z_Zb-^Rb2M5>(or~G{A|VNsN{0w7?fu{k#`QcAPesU3ImtsrOBIz|YeN5EVCHkmj)O zF*lrpb~F8!aMIY&2c;`Rq_@5`k`VIDthbeQaeU%a=)#pcir*)k>n{@WQp_>2Apv#J z^I{_0&PQ2xiio`9G2oSenswDOb*>f9ze<*6y*wS5`6kTLjPj8`3ZIS~LVYc)>OA(c z=x3JiPNi9eD5D&)Whs^SWw!fp{1KIPt@d>?cfB z%54p|o05UUrz1MUYg$)MpX}2GXJVcl*W*2Lb?&q;z4&wCRb$KdXZH8nr_yI#T6`P@wHL?7;8)}!uZ%eX zvB7N5AwMJ-so||~>`}MP%&}ln_OmE>Yu=@h+MAkY7C(Y@K>;}b%LS$Km+n~v-gkLV zgcz!>G6CtR%HdDp?upO}_EQY0$F$kqD_=x;7YD#2)?#>xU5$j^!PokV z+xa9m((r{pM(vvDMhjqOP&2_)Nq1q_gJ(e7XP)iloZMW^akKB@Pjlopqr`Ls|N)50>t}L_ei0okH z^0_Ui`S|H3511zPaPy{qdW!ppd*h6nP(^k|!3bTihZ)+`e#b&zWGY(~#C$O$yqTwz z;d~FpRNJ+iRkL}A$Q00En*C2nuk&TE43XmlLOqmN8!sVMpV~iOINC2&tQ1_S9q-k+ z!{~M>E$>`mD8GzZlrC`FcUe#6?2l%E)7p%1MZQ3I&U-` zK{4is_xJ$U4H}jig)h?ZJq&91L_ACl5s)F%7MElFC>1Z#@Y?phAR4T12uw4+bbG0? zFH>l4>X2V~iTBKa7etTr*X}TG`-I(^mgqY7$%!bfB_ce%Q`tt*q$A`sTX{H9=j8JB zXs@2`bNwh{IF=-ceJB=PMdKvw-IPj(%S(c?;fucRJukP3$%nD7y4|$R&1m0R)}GU z?4Bz&PC+p>*I5`V=&y$PDck#_42gJ0ljxrBvd%VXsBd3)O&xlBYEppDlWV`yxAF?2 z-NQZC5P-H!Hx~)zrimag?}4!Y9684^)ZTv3Dk7;@zI@C)1KC5ZF2V5baNs! zZWro=J<8}TPXu?Qf(zo(PcaaveTko%AZwJCc5c^q3ufK@A_cR1YHBDdrN7gpoY*OR zo7>hQ9wY`$?3F?;Ad6KZQEpJJeRQlNc-x!_P^40v-15fK zrKDdfS?YWi)HXBmOk+?z?!eTbUcGKw<-S(+;C%32osgM1Tx`^hc^q74iuFT%X}xeB zpq2EGw4gg0=G95{r-EM%?DV#XEi^#skxy@Pe!Fc@jD+W0?Wsit>QX*jj1TnfLiYTrP>pdeO5C^7&Q}iT&y5&3sUGX z2#KCgGyYCxBaru_x&mW`(XmkA!cb@Is*bHw69)UoWX?iPUKQ}iq6ecP8N>*N-h%ef zE>QBmgk`}fPZ2FF0f_RC;KtNlL+?2ks5H>h;`$~YA(2?Vb^aq}LYhT4a|t5L4Hx9T zT!gUkw=Z3M9Y|AC?&{>>@+37-l#wOxOluv z-f7w(><9846T&pfv*Sv&UTpFvR22Z}qg0g`1>f+TI{bhQUZ8?I3Z@~II(VuJzKJ(W z<_0wx(|-Xs^kWbPS3w&$*Yn5A9wy2;P4P)hG5^pNlhcVUjKk}JjM$3A4s;P+RWtg)~TZ+gW)xV+6Z7}6Ki5HTH zSZDLs+~&oB0HJFtid};uA4l%b^jid{Ysk<_25nq{cdgphp#uIKeRjt2Wt?Y!O4ehw0 ziSK92#;yc0_eaSpm??}<2Q5*%t-?8_7#mfQ^Z7zmI6sB9B2|%CXTK#AR~ZsN)4INK4dA0B`W3Ic&b7t>eL7VXBI<1+P3yuWe3IZC0y`V9-AA@6^cB{Fc5 zt<`i0U6yxu8KMZuO_YO_%1TP=os#S^e?b&qAT0jpf=Eb0LNG;EQ+!``fku37%QV2e z9Z>w?T5c$%t{nPV{L!(4Rr#j1Dc3)5n08Q&ho_@Ce<~##t5LWBSys%eT%5Zil_{2+ z3Yxu}cvXNvGQ>0yccY#)CjYPzF*ud+slab3qmW0Q-vJrTn8&Cw_#LiG{Fu*4Im%PRp8QU?`q(akA8|u0=JDs@5>a zU^~L{noe@7V@Qcs{590TPaAxgQ``h2^6Wj6WN8#XV+*ns=9k14x#9GC;h1a?q#5%( zu@zW+k)QwziOQ3dAC?&%w2<2e^_&p9DDnKtKgMOtka_PG(Q%5%Tjz(Q&ITwJ9_>iyG0HGo) zWMsMiEvyXQ$T(f;r04JsG4bJuONd>y9UL{7)}4Y(ABQ+p_2W&ygHQ`ZWo=8rfrnS4 z>0CD*B^BUHX|c~N={-V~lY;v)`Dh@{tNlvr-H;hpKt z}XXVhbk_s1>dSSOb=NNIEd=EA;tIZ&>X?s|XK(`Lf=QbwF$dArLtWTFCfzXCi zk#HS9&4NPo!+|V0OAPD?v=+lkN%9D9Fk9poJjh`)g>}{dSiA@xyr_XWh(&KlVZ_ah z_z@v0u0}m`@~XZKidM&z*{@9<4U^+GYBqLGV`{cn4aB>)w1fuJF?JJYNo0?cy(%@V*T*f@wQV@MHMa?num<0s_aK#;^-(4!4^v53{L7rqPjX%kNA0Fs>N1#Xu# z9NYRBVA!4&7wkzbEsZcI0+gv>NmHNC5jM2_8XeH}Jw<}v+i!;=l@w$sDj4(WGWKjT zCyhqzN?gw{%~3&hz>5W_1UR-Phf}!V%=^Sy))I3u!D=UFedwAY!YoWSY-S z+s;mJG*N0TZ+$r915OP<$^1K<6Vb8h_Zku1;n|pG_D`edFw6SjDjyR4v&SVYNt(c6a~e&k(jCpFN>ry(ZyD$F~{aVJUrujM=j?0Oj;n1-{!N5e|)P7xZd80 zv*{lLHZ_puxO2gX{#OO2F`~~%YXxJ>&5Gi(iR0cB;~UwM>9|&B#7WW}X6crDebD}@ z?}JifjX)6A*l{yaxV)glWbE%^8i&gWAbZ_Z=lQ2Z_a8`Ut>=&j;vdBtdrU)Fa!ccU zJ#EBQ{#8b^Q|jjlI<}vCZxfZRZV_muRYY!d6+Zu@nJODK*H@vBCKO*cptV@{I*62} zQv2u-2~pj*e4M&f;?60TLblC1d)%#~nMZNj`*hOML4Yg&6u}LAkDC z{3CH(#yrKZw@xVQ>m114t*+AhvNG%kp3f2VBdW@^7XxroHC7t`D8eqG)K!bynKl46 z^u48M0;xiy-qtVMQM1_Q9$v+_-pTn6f}6Hb6}MlLq_W%B_MB*PEOqHw6wKZ0Nb=(> zHR(iZ(Fy8+U!$Q3nf^rtR*roZ$7{vD#3SO9zoKr|Cz+uVe;55Xs~Ia`6ZxN_SpiD)7tw!GGv>cp z&Da3Fvp+?%{k(*K-SwZ;41nnYU0NU`kYB)215cnyY84#@grDkk@ zQZqnk{o6(VbSb|?|CdW)`;(fn{mp6y_&-K~6|)2Gnd3J`%l0QF1Ax_k>plMz46v-9 zg8$2~*#4wqfamhJ3TFHnt>_s6>@GmN*?!wJ+n-bnkVyQkqS*lU{L`k{09y4gn`Zxm ziZL?%6{7fmz*7E!#QX=A@(XNJ_%|%&AH>YhTM*b&C z<=>dQf8?S6`Lq8E=l9=L=nVV~0(VIW zWh8>PDiZrWXdJD~XF>Qtg-HPBw^QYOJfpU>pv8LJ-E&{Vd?j&aeN}myjTd?Nsm#{; zs?Zi&;U&A_0xR?ItpOIpxQ+o~dgVE_qia9-O@7VFK@&U9Wm&5XzodMu4GYb0ihvGh{|#)RJ(1Rk2~s$mp%aQmW{t3dW}ez!66#{6G_r8+4ICWic)<=``bskFeM z7t$(#sTBBWN1(h6P}Kx-S;w_L5EgfaqLdr?Q~>u=ygO-w-(s^TT1+)lR$`5kxw^bu zJ-%w;mADUJ=XgexR8F+@pnFq3}F!<8G8 z5#5%=maJz0SsENF(QDk^hU(iawe&=izXLh!AQk3{3J`WeG(7Y>_B-JrlAS2YLHCuW zfKSih#nO^jMWlg)-)ffNj`Y+*ZYR69u-6bR&ev%-Obd+o?g__biLBJW572ge)uL9V zkS3o5fgSUbeBZp%#Z3fl1zlpcp}vUHSIHu-*Vps4oT3~VX>hM7{u)Z3bgCcHol^~> zKNK^l=R{tS{fDT3p`0>7{5Qbko>!LRU{f!rY$_RtH#e!`XT?$hvjo<3i;JYkWFHhK zUod+wnK}H@CC-UCx-O;(REwR^6cISQlcnq6>p<(6sr$5-ypo;Kbu^%D$Hv3Qh9q8cp8 z)@2;iCS$Z0H{wu=MmI_8Z5e-XIe#b_p@#zF5+9qs8Ugrr|8gTM15PiLhe-GP{LCeO9sG>!zYcyz+Fu912me~Q z{XFw)8TISn_uyYkj9+Jd5B`;Y|2*?68UA(fdob(oe`ftX_*WA3bIh-l=GVdR!M_6j zuQR^~{|bkIGye^t{3~Mk{}8KwRbKv+N&iQ+;@@J`Kc3lt#407FpR0o3+$dnP*Pq!N z!(S4KzXqp2)Oh|HDE_D5^y^yw9ccW|pZ(9l=?}%7zyA2o!HMZ-I`Z!l|91%G@9!1- z9S9`@I|nn%|B|5uZ1zghY`^>T?h5~~##nFi2UoKiMoPG7k_z#U!`t_2?-2s>Ygkq} zS>|i3&Q7b=LZlcmjA|*X_M93MkO*cTI58uj=>!-tp4YNJ5z%(sU&psQ?q2Fom64Itd^newnSE5+U44} zX<_QbFpYtPSb|VxKZ>v%p0LS57lCg0$`AE%#b3TWyK2f?s#BuY$CP-~ZGtS~iKt^B zE8z>~erSs;{_*~sr2Kb|=yj3DUiAteGs?J` z8y7T6zvOVO`JXn@gF9yWZOWi}5LrcM*}Xe`LU?#^um&-;ip@JcuUC;zXFsAFT*ugJ z%0sG?mtBN*2I^{A{Xy?=)hofnP$VX8U5RE1nLzB(7D;#cbwHpdy?!dc(D{rrS899n z%34U_HiB%q+>EP1)!M~jcCI4v_nCVL;TN9srp=G zKzW`qKU<)4&h?b%E0bF*!BlPX)oy=(G@Xy*FFVV!tA4s1s><_1()c7M*u(b4cO#wd zbBHwr%DWSXQgfonLM7^(WPC>k3Nc%CGnyNrbFNyj9LQ&TS5rZV zOVazg=xn(r7iStLrI4@lc@Jx|V%E&;t8HIA!qr8(CHB9{)e#Ve=+cQXHiuPSXF__L zBUmx(TQS+bWu(EF37_AFt!<*O#ZI;&5;9yQD@$R_nA2!cEW^`$eHp@P;V)C{cKecZ zC6E?-%9V#r!ct?>^B@WB#V(2hYpRSr0f*t&J0t?S?>G!+u()%TGH3L(2>e#t? zY*@(mY3(rTZQ+A5E94=g6%j`J_(k|BjrRr)tez|l&1>^aUq{|1yS(Um@NBYdI+m0% zD86UU+*;$JhvM3kW5qAxpAUfw$1T@@({)Rw zkwKFm%S0Wq(#$jFU+?-@*aM5L zJ}b=(_iP7=?81{)4F23O;{!eWs_V$E+aCy@2}`fDY!9cNJagnlHIr+i$2`XluF~F9 z?jPVli5=W*om!t$fEml|MHoPNT(48B4ySRKbJ802nX)y-t^fVm63>_Ky6#Y z>bs&qD{k}s?h|#$?kiD5;TF0q-|P7jpH6&_S)^rzq>8-hYsV2;q5PI>D+@2*3#-+k zy0g&C1b4^7Ww^68>MV1vLp1p9w%A#?ZX@R*+tX5u96?B_RjNU~6%bYj7nfi=-p;gT%sd)_zN+(MG{0}Mzh?Oe4VSD6-;=`jg^SCnNFv6CfP#6) zQ+4|A5ZQ?DX+vwr$(7)HG;CW4^y(-DmN3t9vG{ZLs#g$L)W|DiDY=8H>Py z@(uSq4f{_8DoEI>xUhRj<`<~=GIZSGit$S}OoFC9zRYncMbbW2U9LBMJKuM!wm}86 zp5TDqiiJWNNa>(}$Pv~M# zBS|)*H?G<+#iN{xKIbm$yr9J}n>GK9$<u59($oeIn?meSQ9xXoRuhk2$!e}_^lPt=xsVYIMt)-&0%6gcHzv`&=Mo~-<56IB zN~n!H?-Q_=_Ghh~iF*2j#RJQ{I^v9{Zbo4@;0Ha;ru8U!Me+{=5gXnOX_kkJ_6kvY zlWc*kl;i}BvsShYT4#~(GL#QagRn%ZQn3<_K5Y1>;*W;D5VI_=N{4(D5}HYEj3~8Y z5eCVCYH&2cDL41oT3?lSwk-U1OP1EN=_0+#q?!f~N{vyGxHBBv*<%4DErDBlVE#R7 z-k6YF`a`u^M3HS3Z}J*@$}uT5r$fF=w=7CYCXRc-K5SF;YyFt`aqoH24RDo)0`%l1k*h|Y2VDRQb^RPw zYTSL>u7L;p(lDh~qmsu|nGEmelKxc6Wu1t&zEk>)K?4Q-3U6Y(-hedgI(zGYQ5=AN z<=@+`alTtif7NqjMZ>c6WYN4v1iE^67&s(cHYB)XtX|e*lpm=LlVMXtXNK3^Md0hH z>)c1~k%hL-ClvKJ_j;tXRS&Gha$-KMtncq)aziI*lg*K93 zGVX5>ls<1JT|d>(kUq9kPZQEtF=W8NlY_>W{gUJ$ydWvM6L^v_$97(^#?k2Z`gji>zw8pgtC!ExBL$Dzc6x?kUC|?rq!bgoj;= zs(TNay3qbop_AjfTa>q5!b0$+e>FzP2pH=0rfH&q7QxLp%f$?Vg$^Q{G zz+_=l?=DzyHP6yIMb0kL0v_c!Sq-K9xR&PUy>=kA^qMra$`}zs(>Mv|pFu~zKA6fG z#08!2j~o2(QgXm576gt?(_E@gI77sdlguq9S=Xw}jTpc#<|csS9%nHiZ4 zT3>+s%+xP0^=C_s8!}$iB5?_m;Z~(bv+_jgAtQ&f`tGF#ltiMHa@FOjW6I$(JQC(ng1&f!dh9l)~ zkMH#%9C0a$Kv+KdQkL=gFVPMjS6-#eM#hK3Y>_G1T;k>9z+)_i5QO9elvw=O#8H$? zI|$b(KWJUW&**vIC-cC_Q%`n3tTCvcsC4ay8!Qcm+U9L|?Z3n5{;`A<{?jU7eQV=P zV=^nJ#r>Den?u{-iKNxe0^3(89BarEMa`#I9P3cTT+xImR$ohP!)3y+rQGknhc2uW zimx1+ZP7*}d#-h1bTE|gYaVX{s~Vi$eI;FW#Q`~1BfZB6Sj|MZuSqka2NaRF zo{x)+d75-JPGzn{kn!wai1OrUmT%RQ{H^#1?}@M^@$A%x_JO7{&hrx2U)0q$ad%CS z=axCN=_Gbc8cRHali?(jll`BTk4K#Grh~Qi{FDMM!$1%bI7dgo&mhRitcNsG*dcDF z*~gOW`JuVx;-*P;i?U}yQ$f76NKtuo`t`5)MQqpJw{`v~a`6g?U$M2HOmNEd4b|9O z8k3Qb_&l?Op?7~S?U(`I>2;0dVIQdLyy`1m_`UyXQ&I)<+Ku9Ml=VWZ3=)&fjet$*W1*p*pkWf_5H#YfNLj!|I)qczoeg2Qk;=Y zmsuXnO~JODbrI-VUq^@{{aLw>)rlj*rGZ=a2LV|s@R{gsf@ks$?1g~NDDs=OJ28z-m(kDnH6npQ?elL!{~csKAmVA)eKuO zqjd)ykxE!7j$g%Mmc3dt#7!*v;F97lRz`4Pt;-~kz>;iVD5B|`~uNeUjyy8uB zgi&!)FZCX+9>;l+hwy$FjJq@mvTH91D|$XWA9G8GeLc))Qo3Z=F7+&+4}Dshygv4a z4Y4L9clTNUd%fda!LHhIw;Rr=H={+Wy>8>nMBC!^d&qF~Gg21^J%vakOf#;k9Xo<> z{cx;CTFG&K)w?_l&H3cm-IdIOgVhO3#_5zj^!aS05&7qNy|>wRSH!nq0}R?Wp#HV~tEt5Sg~Je@r7^Whywpn9AG2TTf(= zBa|DG8H4ZIqoXq1=z%~6S9L!|c{ivDNiIQh!)bZ*K#=KIM0_YIysdLO+SNfPmQm++ z1BVZT$VTv@{m%IXFj2KhAJ*;bnOPbdl0M>c)bFA^cQgcG8m>lP&T{kXh+a%6Fzc^MkxG-eASLbiI}pK@9C!A?d9 zNq2VRiuKG|f;7F5{=pQ^Z!A~8{+pY`2#rrKu!&u&Eq6vx#>%<>LD1g)LoAlf*+NL? zIeA%W4UqpF*_?%olETE6GY)F5)|BUNH?E2}AayJ378(^XlQC>;oMo?$7p8}Wa8c&# zOV+W9=}pIjL2+w(C#{C%1=_dcDr0sbI)JP>aiOYJ;8cBh3PN|p58G$|7$D4=)#pdr z1Zs}f>L3>+Q=+5GXm}u=IQ&@<+(P{JS$$HLOu5W{=-qPde-#Z7;jA<&l5Ejf=|Y0g z?(tnuttc_G!(Y?#t{+aFm?Ylxx8E9Q3ODV~TrjMT{AeiUdo6o#DS9|QmIS`ZhR+o# zW!fg%u4rS#d2Hod^_+y;PP%cm61bk>+(xZsrfYm63BSoUrh|vex?EV}OL_eSH~q7C z0Dk!K1^t;2qu*=cGkUwkr~dc^U{{_C?-fEy;w;r0w>tqNyQ+>f(s!;iqr#~}&}UZ7Jx}>G^<(Ssy&kF8 zC9fV#`m7--92R{0by8J4%0z{I9h%_5X*pw+^f7>$--eJCu;_ z6cEm#r9`?L2`T9ikd~B2y1P?Kx6GT%2lW^C?eksl`#$^w``TykGtXLU z?zP68Ywj`n59|cL%6QFfW6L$oFD`vgDJqX4m^&IzO4a{(p->899F3LRMLyJ82$k=C zru5S3$1tASS9Mo=CG-H$gXdjKggdajD~>Zr^`Y@!uG=LSrRW81zWW68qEud|qib)e>#lTUc|8ZGW53r_ zWYg#jc5*14wA<=3Tj5TQ`$9RZQRLbX7PD9uJ%+?1z3usUiSKZ>jy9N%8GnPs?0IA*OU4$>jlAfoIeF<#yO%0wkyHnMHH50iw0sZr>>1flIT&H4JX!*rbCk~_NxBWK+D5qHgUwebT zKBrBJ8k~~x=WJteIa}S&ikOl;(rowV46yseH{OL?EMvSY@xDGOY+H=pmV__1|DXc) zfH?`_Br1<}(nZ$k9qeQ!zNHS~w!cXpYAo}{H>{Chm$_r|f zan;j4-GY^uxUmGev`T$rQ_v14&(Fm>5DHrDik<~0XtAm}cN~6$4UT`Ltj~(39P|Ub zZ(~0BlbkuRz?**5w6CD_t!hm_xyEt{N_;sOY03%L^2L?bwNc5qE+?^w;svV;l3jcT zw2NmB?yekM{TTOoiqNgYir5zfvG!pdY{HAW8C-f(Twe3%)q z7*lK%ee;ybH#PFD|A-=%t7ZOv=d0lZmCvfHk(`Ziji=S7pl5C@Q@Ku3$`|Q0QVlpf z?=8oyTaw^ATwNBH!$pOTt}YYJX)v?2f_!2ko?q8$&{*sit_%)X)$5{lV||>T@s6KZ zMc>F}yf-Z3PvxvbL2tS^>iG~sO~^={FYOTqSiR9J>q28*|cVokVtLEmIXT|CFn=E zSG=UG(u&PZ#L*1gQuCsH*lZT4uA9V&CU#X>e*Fu3H^ftxC@Lv!N7&*#*9MFL)6|v$ z;`LRMa@d2&-Ey=#I%}@z&o^ByuO69UKntB25WU_LAdzD*~d%dk%?@c+S2{#_=XlD%s>q+ zSCZ$GP;u=s>eb9XkQg6&=R?j&TWD+!A9@7i3YXneCRgb$0)~!g;;OcB;bi`lFe4-u zPo$XUnV7Z!n=Q`1@%@-B)N{+5$+u1&j#qmcG#H+QD|psjcMM z!tW}Cw61W@(d)Xh>pK&ZPKOQ$^*fdI2M94&rggSVSdutfINMWo zQLvCk$Vy$VvWk9<`q00bQET)0r5X(0L$Ox*Q*L~N*&sMa)nmaqs01;#oI3i_j4q9e z78-LI@WVX+X$3MIb=Qg=WE|l>sk$#qgC8%nZUtA@- zu+^Pal*Rt>v@m&w6OxDG5;-}0Z-l&fUZPi^3$s#Pw-_XY746_8hiI;%wM)t$bo|oA zybXp_O6LX*BWJ&^B1nF|CsbV_y5k-%Dq6_+(6;%MzFh(Xz&$Z(>6CB+RjfV++JFCK zyW>mB10Q;)%j>v#Gn@Vw$ywjyfjtKa0C`FYX%=oe%^NCRsN}XLi}9pBpRGnq#%q!g z%6YSXb)+|^pP7))x?iBhwJytT9{4!9v`Q;}SW@ehyx5nRar`j}a{FhIha@Q|CD|nT z`|kG2XzUG{p@IdiX7BAQL(jb)qeMMcJ6h-={pxPUd92i1u-ddxMMBB5@V&b_KOfjW?C?K1uE{*1E+3+8l=%?bbmtwLm zantLSeq(hP85n*+95HK4J}ej`jTl=cydPSalT%U^!^_2)g|50)SG+zJg?C)8VNpY3 zV(yxH&Wddw?D?rm?-dO4hXM|9GHE7N9n8HxyeHek=w`}~Vek&s^_{4;g=wjoX`dK- z^+dB`Zh8<%#mqm*R^QX`Y+zZ8tb35IA$^so9@aVh-j~kqJea{en@wRZHhw7<&GN9C z8_glPdB>NyWMgvOgC0=zCRH{6!KXjhx&vimm**<@y&`2 zq29%D(E?+Vwm$=rF@oDJ@ez@N0NDF$@OzANhN)tT-V0fcAM5Fd5st<(#_YABaL&=I%=(PHUoU1V|}vY`=dZDe=Ae2+^49D|+UaE!W2#;nj!T z>?sSc1hYpoFdjeFUW|fLc!%6hu^wqk`yiuBSM#q^%XSwD16GW;0&E}SzO%M$` z%)aP}lDjvCRoW!kmoWqcaKy?xR9iWW4{_u=!rfru;PKWgf;=La#Vgy#FivcZUzX{W zRK6keyg+t%<5?hw2MXBba?X#(hMLp;mV=F+Mu!yV2R4f#W)^Au5}6aA77Lvy*F@L` z+w_)Em>d>+$9F8w@3C3PtNRoo<81OCqWYZLgEkz?@S4*TuBQ#Vb|s6T<2V%L0P<&F zi|FnJDt0p&Bb8jEGDqlo_IN=*$feI;v57rJE9Mknq`x;B_T=2XYpoGw^oy3)=B4Ob zfqiNj-%f2JI2!DoF)TY7REBqdO)K;{8*bQ~iyyCEGr_0O^e=^ZMMW(%Kewb|stA7V zNQ<)vvVi@p)=tUd<&p5;Qv(r(v}uHLaR(a8QSM(`c&e}5ELn*(2aS8*W;S#iN$Dj~ z=|a~IKInKAiz2#hID`~%Y}-H!f!&K8EqRKsm3vX?)r^~8QrHqhS>|6VC?ZDWS61hF zfWFSHqS_Fc)3~h$Js#tS<=z4Hj3Tn5mQ{VypRyThxXs+;4wCQN7b=zi_qD3+lU!}}a* zRmb;CXpR)JRZTB$^gBNFHnvaKUtm$oK(aJnzm;>majlPqr1XJY%Ewsh8R?$Ku!noN z-^y2?@Cm;Ffy{ls3=HH4I>ZYS9r-kvMa#+)NBrit_euf`j@OBK2{dal>e(_p_aO%#xQ8=Q;2LUu~jXl zrCfTbZe0=8sk_t7wu+@(wq&yu)Q@=>p2N;*w0C&Dh1#i|GQt%6q5JqRIF``kX#3pH zn~#QV-(HEM)m($&Uf+{yi0@+4f95!pG@%scvXl1Zq7PS`kc{<1IopA`NQc8kyfTv0 znPOpiWWaVY6~j9zMbn*H$y%!8J!hX9X(c-CU3Qye?cy1`rKfsL)u$yo=A^=vZ_u%DwEfQA=#oUJe-}Z!$$&I%iR6Ga;}pTu=f#;9uaPZJnhVcR<{Y z4mfzb_}Hxv+!Dk;)T!*7ZCSY0vGP*{Zfal@2rL`5hr>Uhv;ln{=~wz*7dlg(j;z+v zM2+F_A@PZ6MtRh>D5tprIN^rKF#e&+Oc$^JlSMm+Ictp%PJPw|iLqs9d5zvlou4mNt(wFyQ33DM%KiXT4 zqWdgORhUIja{lHf{QEJUxk10q0CE!?VPwDf?*0q)p)he z$+YKa=T0pnKF_J?P(pXiTV%nbHtkMj#k&`0KD$JT0S(V-sC?E95^v7pF*i-6ztimsy;@-VelgowHT_M zF2Y>qK|hD*%C3E-XOQQ@9H?vgtq2ukw1}ZY%oTS$kz}f5GgvyTwZZP*m+Z0V0~{?= zPgh4TS6)BrXr7&N?=HNWiS$zSNC9gX9QsEujd8GI9Y^-x=L=;GH3qJl^f7<+$NfCB z{8%sz?Q%zge7aY*HPAh{znR|d6IEknj_a)H0R6Y+8mla|@=Z{{>fnp-8oI+3(M)@B zOr{3j<#qv?ktJ%ST<{2g|%Y0dX+J?QYNO^!N8NpFVmaN#>ZG9>Q%Grmrh zvGAb+?9l@i{sJDCjnoq!v-BfkmW>y$)7IgmhyypITv##OZE%xlbgnzrsAfy9z^rAx zl$Yg1=(vSo=LuXMe4|+lPhJg6Nn4Rxjid}Cb{Rj7BF>rX4Z4;Bk^^pZegthBY^TS< z^KSiHgPop(dJS-o$cn)`BB}bg4_uvHKSm)a$nyH1V0L4#eT;WYHjo>CJeb28AEN^3 zo5mKTe^Gf72xVI!5vaYsZcLpWV~_z?{b@P_zR^cIPQWjJ>ACqUCg@-WFk-xEWEJhy zm?P|0H(E_X+XR#>M~$g@b266wm{rTFBTlR5NWr@}PU@PfB{=b#u~f6h>elRDtj9Y| z0@WI)B{lC}jE@&#*WvX=cJlbMdP}f>^I9l!I(bvq<_bG?6iZFmdnPEs8uD>c8H&VQ zg4y4EvE_*l&0z}aM%Un*3xQ6!rcWnfG!6}6%+d`OUX15mUc{-nI9)#$W%UnFF4ivM zJF>@eYf$*|Vyo-Q1+sh*?5rlaHFq@JX3h`dP%h?5*|j>nm*f}b!xA(PYzZGeGws#> zAUSF8R*1_B(~1WTv!o_yUpt_zh0-~{;%hs6PLt=};iHmo341$6QF9#iw%!+Sa7dF(zk`;P#Uqhm zCsD+@WW<(f5QfhD9Z>loE~g%we-K(o-2<-r0mY%!>V`wb!16O=1%9H4d6G&66n` z`pk?UQ_0-2SleuaP@6EQbKe_^!HOK5b-j90Pz83HqTM%Ky&L_(y)5=+_BR3*B_#mjIBBPxk2f1eMyQ~@$hW!QFc;3Sk_L*x5xdp$e z<&!z2!Z@->;+<=_^be#BMCUJS%D@;~kCt6(L#aU`dA{hDMCiWWLm+EJmZavKu`=w{ zmNrTkS(&Ag88V~yU+v91Oq+yKJncHzfK6PUk9t7nJp6k)Q1REJA$k^Cn4MD+W6!uD zGw?hn7@u3GYP1^-x!R(HIC1&Q)NIe@r%6VbVZm$^`voD%!r$WRie;|MJRWb#~)0KkUuW>kNb zPnL37PA+m-Wer<@A+#LvTYS>0XMjobxE6+{eKB&Rt|>arAH%2?DS)5?{RQ5a5OlzU zQh}n(4UgYtg=Rdgv5b*t#(q8J!@oijCcY1U<4t=$%b10|gT#mfl6Y(s)-ps-b)TOqb_a7quW5e;;91qI`dt(W9!9l)mw65?K{f-YfX%>tKh5c- zbyB#~BS{g`hkh{eGhTH8WIW^@%7my#Dbe~QsHPFNRw&kI1-dFp5$L3-)!;D|FDP$w z7L(@--a>eEGx|2QD4^80v^3H!^0){+sU&{2F9sY)U>xvXJ)=O8)35tk28@DvIMTG3 zditRGCA^%6UTy%83wS4Jd4j*;CqD9MdSEcg0r*90Y?Ec8Q&U}n0tDAB)25S2dO^&3 zTMwOg_?928F2{7L`!{Tj@Zx-XTo@x-ycJHOc%4$CiYJKSm`_4lEG(q8`&suKf zLnD2h8HLLw?!y|MMv->=h{os_C>hl@CgVl2h^#YqoUW0HM6$U>DBqWmKB~&`=d^QA zVr@4~C0sgA{NQ`#YU98n#-jm(Jl_*_lPgy>i!I4_2|Bt%_n*$5fjm;T_PJnk4WUc6 z9?>9hN_(IXAUsyCtiX|j{VuDi_MS4%jiocZ8IRdR&sA(WACtz*Bx6>8N3f^o{4wl* z79f<4;uQuF3Br`B+L}^4$a(h+@E1)Wg#FBvQfKHh_35>25V1*FL&{XsXEQ9x3cTs{ za>KP*TMjC?Rhk{G*B}mW8oPoY3_>oWRs5d)iq27+1DCJRCAVkXAI%6Heh$tATH5@mY zSgdY?6moM+D_rO?a*>~1Gjfm%v4#~?@|+i(L?*-aDRjW<3L4i$P|PJz-GB3_Er{#~ zk^01X59r)yU>NRa7dKM!$D}7CO}tTVX@#X2=eSQk?P>4}*8gxiTVcRwt>)ggPj474 zPI^AhP@FTRHe{@^ZJaWU+0?6|?`rP-bkc(CJ1Tj^?Yh*?$tm8RKnoUHOePF6=+mKY z2ro0+-k$I-2tFQX$tw#5j|$fJAQM?hOLZAb;~FM?@#f&d)#ePdH05F>i1)}0B3fB zxKTlq7GH|`0*6(~uz^^j#T5DxMbzNsBz#m?Ckk2A%Imii7SOnTa*vaeBc#*983AT5 zpm-ja1k;p<6&EdXqF=QqGA~S=TR3lY;ju?m`DC!CY$35lRhC0R^Wi#gu9BP4yoK)m z5TlQ_eQBhM2oo%c3QD^W3PKeuj}tQfkqM&hjpE$Zn#3+_CEcK2-z7z=NDD^Us)kNo zyxMR=;5ky1RtdJ|HTmKf#`88u;Z$Tjiq4A%3`s8^fZd*nJ`S|@3-}6CtSEx&L-07U zVkIp+14}>EedvvFAYQ;Fe!ic$3i32gO=O65Qq@<}5}55O+xe_cWh2}bgS8^?i^|~U z@-IRB*kd5BT51!+>SH=-!hAi^TK_utfYqq@DVxfgZ$fOO?;b?tJ!?cnFV~&_6!(dd zpq&eFxKsl#LX5qaOhWoyLMO&G*6K*4%&}p_T17I*L7HJ`~aBv4Fu1PIxat^&&Dg2(D{1VXVNDuu-_|# z2HMzXCpa74<56ScmCeUNMLpoHghI4|&7`B;Qx4#K>22r45B-YfA)6MdUj$?D5u?}6E7aF*+PG7QtJ#kPrC;!%Kb0N>ae z?Tkw)3W?k!1@W3s%xP##LMC{|I@iFibq%wo>%+mxp~vWf^V|E+o?v|5cgr4IpXYe1 zqx+4IB@sI<*QUcD%ec|jJRgJ&_kx`M4Yy}zoisO0@9OW0{=t?u6*Jfb`%%^MfeqFV zMG-^7Zkit*d*Ex{OeMqf^y%{Oa5A;4S3W8xRlBzr2*-hHGFp2u*f>R;tV$$ujKq5S zKt#J#uXZQA&+9bkp3VpA;F3|B=2|C;?hPvk&-4x;%%*QtMnOi4eP{+%L$Ft6xX2Cd znh+}>IVrCEC}dk*DK4TXEaWwTNu1|NDvmPe$5eIE0GSy(5kjtA_>|{0*XLht=MIiW93%qQyEgw)@we)jFwed6u z#fM&F>(l9Hc+UmS&}{Jx{$PB>33d4mtk?eP*(1DuOx6w9w)3+E{T4{e+zmE}Q!`7k zq^vSj_F#Ho_YS*%=kV6-5=^f-jKhb;>qG36F**!$9SgQvU~%2loTXpaQ4HI?cK?*= zW}S*>au2o0SKLq!PVdc7un#ZJ1JP3Pq=+8T3Sd?f=BRB)x=lc$U`Cq4kTfTJ zVlQ=A!|k0cI{7L)C6BibCNI9rI9HWK+>~XN_%QA(poVd%VP!%}EeGo-%VTPba{s|2 z6`06VSwt&J*aOj)?*HgN2KlC=D8eAj_^-T(G|JDMZ1sL;kL=7X|H{oZAbgBsXBHS- zN$vC#+cgZ=T1T5{w^+zya6F&STR{KOOdpu|8BH+b<>ktg;{6E>w|o*K-btjq_eGK9 z&@ibOlSCzA7-yi|L#($-;r?4?XUq7F!V+QFf@gu7~ZX>A)ELh1d9A z$BwvA*#3MA_(?D6`U_Xqr5B!!)jxMiSSUQd>Jak9?dAv{x=a|FXYUUps7l4f%jq*Or1=gSmDH2-#rPk)YzHIbJXJX&;M`0n z+4lEdOTJ+AhBD;zu>ILeII7U1NderGO&2 zOlNS8q(5PYXDqR$?JBp7Lur>P_aK8bjovXM(+Bk_UFCvkr`UTvE{eQ_;PS!Tc@{tj zu9JVS`~wQhTWg?jsVwgnZg)3lfA3W*7hO%mXX;56kUHFA_r6W}E3`u$Bi>J6iQ zNo7A@&)U{*d(@gDk>3ApCT|3N9Q+`DLZiMQjy*PrIpy6sQD8s!5!>`Rx7{ki>u!#o zdi&Sh&gY5eeyNQG&B-;r{mS=%Wd7tTLJ7;znedOZfODNX-;?tidV3s<muZ%A-{e7X-2}lQCr^ugnXyejQ$ocx*2R2%MbRU*GwNd|P zq~vStmH?WWcnkcy2|)cO{`I%6nI;^ zup*hgrGKx?uR0{f8W*W?I@}t2TBv{*wYn*CG~OkJN)X1@e$`xOn;JBl;-Qc}=br57 z?Bl-wcypI51fkq`>6C@w%S4`lWCbyM&*9dlBAvCYb8IB>b9y`-8f!UEPv&ofIZu7( zKfG*Kfrbf>>Cg_Bo4~Mo5%j?k`~tj&^k#993bXyb(~@n#Xl^;YTsO?W%QUgkb(m;7 zKJ6B@#eJ>drg-2>VA%g_3aeophB(X5LQP96W4WCgA8o4h z7W&0Wk$a14o^vn ziHau1@3uwE2L?P$dr)b;&cdmpBBzzcgaVW!nE8^Pnz=iE_bn@aqUsZeB@%xe;_gOj zmcE{kXv2po8UbXi?if2Nc#2L&a7NPe>-8@c1e9Kv?T4GubC4iTSD&a2VmZHWqSyRb ziZ9Ji!;($qD8%aD>z(qJF6bV0?QCiJv$w4)`1a88q#F7^6DOi!thUjghI+YdwIz(s z12@dF*#>80Ul$sBO?@&2>aL7R8kS;x?h{*a$BCLA>0vAt=MOzp|5U!vvm5MJZh1J{ zI*~c+81zK!Es&Mpe5_ei_D~y3LFIc|u!x(pT^-S|6A4{;84Vsf-TF{jr4mUB|5pcp zaQI5fufz@f)dPV+=!7+kTtU`>QtG+lEz0;VNO%f-;ih*$WYvn0I{~ zN13s+R<^5LGL^%^{@ZRf)vc$vf-P?`M#A289L|y;Y)GKOKXw#+p!4TvlIcNJ!THMi?5hAhiqn?UV8G z&jYDR-!aRQo;u|I5iSKz}BdUk=0GanOrjFq}uL-ADm@{ zA9sj-blK=}(Mkt22Hbet{00r~$h&}6;_~d*9CJqcGJ?sza7OC$S(FqG;=aPmtk1x5 zKfOg);Y)Cy2||Z6$Me67>FkPQkt{1>xr^=Nt3tbNva;LzEy|v^?Q9vLo?`ZWaOJcU z;pvk2a8MzLiuD#QmVlI0J$k78+bPf|nZl*w_OU&=MfwT+d(rk^ydjWB7x7Rbi;qNOMkmY>LMtGfQwJW<#=BRhmT zD#uv4T>YK#D23TpcWNkkc&ZIK5LXxf^!e^GgZ5r5^(U|UreL~~KTA4ioXDG7N!`3s zo98f(A665?-+bTKbHKYgTfb4|DZ}KIm(Z)*4KMjUBRHdtLyAK~UnELgmqQ`5FFg5! z?QIEgkVAxx54htcfx;qx9>Ld_c~;`A^dsxSp7kHCV_@w%p5%3fvP~#VR0O6L?W;=D zKlfRkoqM6hlys$!WAP$b-1*Egcc_C!q<-%~D^C%FTw=}_CdS_ecbu!WeL#zFz8 zlxlNtCNQF49h%>tdXIjB-`N!}Adqkmwe9hxqbYt>(L+-C71boRV^t#BA`Xd{le(fP zTF5U!-Bu!}o3}+Y8Sz<<@{wYz=7kAWKUhnR5Ms9EM>4XhukY;3b4zxW&V-n2ru0x> zF3#ko&9fHuq0N0sQ;TEQhlas>xw0~I_%yR-@c_MJfYa*p*(Gh9;|)BjT?-)+_1qQG zyiPD#Z)d|73__AE5ub_#= zBLT{ay3VhQS3==Im-TK#$EVUKbVLaTluXwCIDUBOZH;DK%$m?_IbceslJ=XUnbuc@ zFX!m^s1Ymp*bb_Crc`!RHI5XusPXnFB8>g_6)u8z@6&}YQmQ7mDK#I(zRZ;oH}Krm z!n`fY$0bWeFo|*GW9ijg2wITdyuVOS0t7N>5d|u>#z}!wK&~Tg*yIbr_IoW88~j}n zM$0!ZZDoeJS||Fr+J>z+moSY}hwabM;E<=p$7A#uMI77(uq#fyjCs5^+_d2>-^pkX zRMN$I)27n&-aDiao0uo2m3B*^es?ddOac$1tP4)R4MjoLW6fapXSQgcU2wsrjQb-^ zIz^?ijquwqqsw8D91mh7^Za10dC)-@q{>-T+Del1TYW<* z1ge$rrV9Gx(a8oZcF#@Q<`8j%@l~Z z%FhY^IYXAwFM`wXT8oxi8$=fc$i3x(cNFk9{KZGiUJQzoY&}rpoZajQq|$~Z91Wez zYu9;{;=yi$H%-)zx9rs+Fm#UPA8w+uj=7XZO$1{g! zf;WQ%*Y7_2#oU|ac))=N@Arf^z8CI`X<&rxQ-gW>enTHflInSAvu+7>b#I&e(1G$U z&|K^5;OJ-BW;4XBJX|lHz2Q?{u8Pq#LeP~#be$v~#p&Z|uyru0ikzAm&FT|V?jN&~ zYb|!UI$3Zf2NBj3ksy+@(9O3OTphM&s(M*A1ip^deWv9}J{#G7{I*r=W8)VERKK1! zW^y@cMCd`@^cNt%D8-4lP-Mxj&=goO#lMDuHD!PW1yd}0GyeoZL&4+(pB~73pYs@v zhM&S1w*3IJ7a4K%60A3(F=T2tcV7?Sl$CK|T$ICM)z-t+5Ugx#>$9`e_-oI=C}DZy z;1*#pnftz*%Y>7AYVkC_g`N%`ee9Up67c~*Q!vT}bsM2VOBXYPVl~&>5WRr{79iCQ z@kZWUa{bG^j-U=60&t^0hk-foCvv$oW!Kg8*p!y%wuou;P6I z)=^kc!a{8mw>b~46pA*ZCK(ETy^AX+R7UcyUUyRyWMh;9v(tRq;>0-@>O%2FgW0g# zAjCDgJv79UBAb;unBqiei!AagPfp40R}W?K{626pNLWcdgwbf z%>fWtLwMvl0DAMknU}f#!Ek11X>dyw2Jm&=t!aP<7{Xo;VgdNvxqs75bNz`amHVGm zns-1W&jGXO1x^Hh?tnmirj;bp1sl&;2JK1L()^w8J6p4RJ7tdxQQq{Rbxlv)r+h zv9ke8?f{EBJHRClV*SPB4xx4buMP(TJm7cQYJfQY=Trs;%>SOsAgsiHFnz~v#s(-N zu(5;5*Z>}6Htt_C31GnO|6ux#-3$bXAFu+?5(r?DWMlcO!~fuBV3s>}Ggg4k@>Z7u zLZ|*)mjdGQ|J`a}Xzp~Z*#LrPFhEer4iOCC_}yyIpWF=4a=25|tRRRU1u&BV^vZv? z8X&X(L#yxD%~*lfLU=eKEXx3c|IZZ@4EmFs0aNYWwi+-Ep;L#L{!1JJ{7+8M-zz2< z=%2ea4L}n%ppk6A^TY=JtJQyUGw>a|87p8KB9Qv~niUvUR$vlk1=_?0`t4>v>D~Xs^uI{X?*N&x zLgsLYX+U=5Z_~dylK<6e);o4H2<~SER2o=;)tL3SBnJ35DC zuRI*0(g5NHRxJR8g8u?YKS`ng!^7`d!U_zu1q8SnWXu8J_iN<>djG#W{LUq;fN=%^ z;t*_r5(en6*$g=O2h(@FXaabQ3?N! zOOXT8`~PkFt|hD>6DbH-uOSmD=(p4j_)ij6;JcQvf=r~hrh$p{AEy80W_K-NbvxFO zRSPoKe=Q$>lCT2bwS*NU`T+7m5C{LQHv|5YfE6UybEg;u1X(x$qzMLqH!H{Q8T?NI zRscr7ThoA47qDbPA}#={{Fe6t|4G0KeAfb296(A4Vj7}6^AFR1axwtJ-D!tIMj8Y> z*${XHsPO%oM*k#W1-{iKxWgqA(rSolNUQ&0`cHG&trEf=nq~vmD>h*9faDg~IDbdi zKyKr|=d!yNumVHkOknYVL|x#&0)RgWSV6QQ?{uthO#@Ttt?9pJaG)Fh(CRyOGoTkC zm=FX+8-HWMKPgy&?^?l%9U_Sb!T*4i)!+F4Pa0O>yVkH`2S6`GNQxbJwEq&40>=Cg z{e3Iiafc3mD}M+9W&q^>8<_o3!|JZ{|Ll+yA%GJ&K>x%Ef6D&@+D`xQ@c$P{5{Q@n zrzFWw%>6G(lAppZkkI4*RzB+=WA)F-1#sp6d@}#37V8xL z`RC1lRwx0w7y?Uw+x#V;1>)dhgY<=|g`J@d8MCN`o}Hnvp}v)Y;ZM+RYiDDq_X-W! zDY-bY-*Slqvtt+j68%ZF|A@_L+83%FXsAgV+~gg?Bl&c?>M#6m^bygIg3{iH{xG0~ zawl;k>2qa0syW=y{Bd?1wEoz`e(Z4L%z;0D=j6wkfs5(KDS#!era z9-nGEJn_-4-8or1J30&6eP4g&&VRLceNG{A>G4C~@j_oX0O|Oqvk%8+q3e-c{j6gA z_51^Qqx7v8v?;+>zF`pssdlv($1AUdS)oTHqo2Y>J}swG>zpj14i|?W(Zou-Z;~F< zPaym{0^a_>J$rPuU%r_V4O`0FcWX-n296@6UtDl=gPH%nJEzgOi1g{|$i2FRqtGU!+++)i=)sNhu*tM>R(VX)4S_6gy z9FPGf2b42y=$>vUp$|-+E7-3#I6Y^j=AE{6Y39bBeKAKU5XT%wk(lQanVLMpSCpXg zT&waW-$_pa?({;SWF&k`ZLvIs>Am#_hrHLvESRt z@6_ab*u5qfQN8o@@wgwUv3VK4ZD+u44~s^a1ia~6gu1=pTJ+q?u5(74g(v&I7DpP; z2JI4;VyX#|QY*`fT17fh@1My!v~>t6V&EoMyM@u(NJI^?kh^j7>qtyUgHs1}I)Bi2 z*quwBJA^_Ne%8TJR^(LmJ#ANE=!=<}Hp0OknOeoEsmofX`;xiawv-*O7|mx_6ru8_ z@2HNpc?=6{dT#^MULEaYGk+8kD-(E#{ z&qiWl&^@ItdGvvvb9STZdm?=7pwes(Sx8eD27{XStFN(I0gM%xzR5OCnDL_ss|Tv` zsZ@3|jHuJ#itxB9>xI`;11%>P^_uY?n0nIMW?uKhO!3k7RND8$jPaSvwexu=BFaq+ zuC8ywTr_Fv^&2qtKEX=2wVX(N(^S#dEw2QVYM*HuKVt7GU4$ybhZN>-D76~<0&IEK(TfbL*)nSfK1vbP_H6kUeS3oA)U!1>l_wqW z1=A*MG!uxs`6&;|oZ;g)IH+P)!7-=G z#{oQ>(!6uE-{8Dp*rDc}N~N5*6r ztzqKzMmS&69$AKrl$W|{kG}8qoRS>nHx2Zxm>&#C^RKi~OdgF5AgOscZMZCnw<4<$ zRMW`67dXOVSSZGLr6QP1B4VXdZ2a93b0Oh}{Z}LY^Nf#(@AXN=q%_{W)u?8JK6{#i zd(1<_swn8*EaiLI8WGACA1l!7olr&l9r%NTV=zh?hGAp&DQcweWtdj1LWc9w<@Jf( zoBJjGay2K?@>lt)Bo>Cpc^V1VCs6AOoJ@SE+Wt1}^97-F3m*CiB?o?@DkA-M*sh2T z3bj|#{#51d;C-=|KJU`z8ll-=<_$T8DSjZ%DU$6+x@M4nnapPR_Trsu4(EcnPdzNO!b~wIgx2X68T&}iWi71ZeuA+ODj8DGEG3c?G|SNU-7f*;r0NM z9J)P#MA49g+YS2LgJ1SQx7*jZXMSINyR{3P`MJ@0d+_UGFc8Dteh(Pfsr_~E>zg4v znLp3mZeBs|`Ypx=zTMclJ@fnG+f5J1ncFRh+k@ZVe4C!XJ@fnGpBj6>#kXJ%^3g3i zx;^;)Gk$8v0UzBaAR!;!rUP#eet$C%iv9eBx7zEFkA4g4{Y>iIp80+8Z$Z7E2?)qX zzXkPv2IseDeqa1sP!IfDP!IfDQ154m1-TfA4t`qyEvWZ18i0Isi_LEje)Tgju>S;u zK*J$7|H4@R4>0y;%l|_K_Ge=4UoiG($NUG3{l8MyyS4Ni4E}sh{)r*~C)|4bTmJuV z`=4>^?XUiyZ~ilGz0GL;`=MR$iuyV$@6mfL+;e~+sA@zz zg?iET+?+dQ>h)C3T(CF0+|AmQ)%9hG$4QOA&DH1Y)rX&NT6;cEr(R!$?d|T(kzUW- ztRr1ttmw?0O*|6~?Vcq+LdHHaNYkFjuqmDFldG}%T$XiHfxkuoEAYA9;Tq%f)k%`a zMSrJ7v{A&9B^msc=tsTp8B?!IkPlK*!@Z+hlt9j<70I4Fk&xo#=>_JvC_cN{L_`c@*^qrc7DKRa}OZW5Enz?vEp1FJJdtWA; zu%zWiIpq(BF;chEZuVeM%So@df#yhG=H-!IBM>`yoV5xqcx4RFz2gY5cs5B~Zo1?* z*js2MZ5#MmZJT2N+VH}9Jd#cL6EWW(&;u54HZ>}ST< zIU{Ub8#hmJavZv`v16{apMTSisLdqs?QX!aH!;R$f0LFjx)z^#B%6SY;UcL(_u#p5 zmi$r_<{?YaAgG_HOi2F)#;oOVR02X?9$8<%!}kEmm>0HMMXDm)e7#v?SNEGQHS%0- z3*%EUd9rA&o0itSc;|MB=Y<)SzY*7+Jo+APuoMNJUt&~I_CfzH(f^F^EEFv(#xiC# zkLOs}=NbD*j@fAB^H98FP#sHO%RR#En(ktydQ0;c>hu#mQRAXIS=uC z6zp|_Eyz5L{gl+D3)AIlOyP35 z#DJr5wSkeJfc?Jan<+$SkJAJtOmq*Uz5|e!a@f3jLd>bm=b;XI>4tdaa)?>LaU=8U zg-Wx)HRiIda1btJVtPzc`zUyiwjA_bH5yfWObLw*6GXFA7_;=sCdTYwh>uAS zy&Um8%p*7*5G(bpFb_7_ggZ!zqg}X6rgT6wyS}b5?XH;EZrQqy6BykZ*ipJUn|3|d zVc!!l(^V-{iGaV%xzU03P{}%bVSYU|xpj*FL7}t_e(y(>4nHY@S;E;WOW5^yNA}HW zvjQ%GSQ?}(B_;L9!gMo22D08)IN2nnF;x!k-wG@l)Q30KU?XIb+~>pi(~(+HxEx>3N#oSxzz>rhBkY@^-Q%q$9<&(ZmQXitVd zuQLMAg0+34^3cmovDY#_S?6@uC&r-ryPHA+!BbXu;(>X-iud{;>Gd^%>wV5amX5>( zJ7uxwm;QNdYWA0K0|TBNy8}X#6PYvt{?dZxOhi9pLiut zQ!m_(V6z&w_IZnku7RR`!$CGGVv%_2evi{D=9?8<{h_Y$K$s-#z#ESr#V&=WJFq@> zNS8T0Y;cG7i&f&i=5Flfl24{+;zV?VoNRJ_4Bu!c4GIVd(EA(^E2!HYZBAyJ@b-+j zlZcN)5rCv$f_tiMu)U*Z7w;EZGLa4>eq&)^FJm%mI~h{6l<&hpcEH3eZK!NfSdJTmMKHr7Wd#z#u;ek$8mnK0}H!Tv1h!Dyms| zkw294c(Wlos!_C_sLvs~GbW|pbEa$;#SUkG3u!2gGgyzB9=ZdPr?M!E4>=8mpQUD~ zqtD)B^L{(bHZ=P7OVqn3XBQMLhm*nHOyX4wA)dqOMNSloXCbJ`rdSIw2cI5M*b#a7 z6}*eKf#WZ!vE8h~`i!P!0mYe_5K^e>^s21Su-u*ILM5JHj}-KPC$eqAZY{It!ddUK z^4oQs==YF#?xG}t8<7`l>-b-zp%W_U-&;Ph3rywUfb$TGl~}}EpChr8eB-w1bfbh* z@-gDfKIh^X=8MK|Y^{WUr&+vNrMpeR^C2(O!y>8(Y`P5x@6v{b)Y3arvr!;N?=Tl>)A-)Q*NM3)0bitr2P&&B0rFjgP~; zB+e8!{_1@nNuvsOk3Jp=#IB97Uy$I%w7Ew$l07s#`xYmg%Z%6<(lM=jl+F4$`~z&B z7UO|@;%6T{)hxNYqq{$Nc5!~hJGl!Ey8EMB(=Y^aUHpiE+%k+LSn?etB zQ&tDrq=yqFgr0J989(9Tq*~V(=gQN6A@2C(>nNt9?PF_S7CGWNpLb_86;))X&-^7h zA9D+K>n#se zVZD4Ro68BEdB;!bM)v4k}+!V#hh3eV1kap*@H>&WFa=cN%C%gQ_-f}o$oXr>bbt37| zdou`&T)VsT6V9PWQRBXZsAOYp<&;%fy$z^Qu-cxvq0)AFgSO5rcmzD$Ub`DDfzy2Z z+~b-R2XjsVCMXs>$0J`0p!G;t!xthsy7iNCAWnxe7OnD!#lXo=QcXq=L?0u!?8d zY7r(EZFtctMOO@r_`kR{^N8;izDKW0{W;pkLXuGW(qXL(++9pZ+Kqv6;h^+|An=uf zCN2ykJ?st^n1g4B1>r3&K;k@WbbFrTH@kO=0l``rRUwJwpDnuqJZX>jwQE3 zLNU9ZMh!`MoSRcAx-0(Z*&ZbR8-X5=;~@y{`GX-5j@&#*{6j|`7AjFr{L8`ybH+T6 z0|wfj#Tw#}hL-~(k%F6gUX&`tQGh49O2VHIw8SCXms=C&C3f{WzYt!$O;nY^3YzO| zaEF8>zt9%B9JEeId(R= z2_tdI*Ni8MKB*=Cs6R>dSLb{@cGf8-$A0iA9v$ZCjM-2QzVJD5tzo6}sD@g16xfdr z;YHSjzr1E*T^63=sArmg8?Jpd72?&F(nCf+EtdXaf_Ok->j=r!ghOk zO)WUFIrvu}L+l2a6f>Qec8;wfH0CZ3be4At$$W3OPGZRlripJZ*VFx~UtbQph%T-P z?vwyu%Q+I;s&%p=bQ$S7Mf8NO{%MI^QXJuYy)F2gueI?|N1d=!B50BLM^A94+A&@` zmXbnVO<88VasMRygH4_mnZ;s#R>UcX)AXw7D(ES=9(rD>BW5k5&9a`)VIrJS$0iiY z>YkjVT+m=3?&nTQBS+1^IOxaUJEY~-w6c|8dA5py0s;SyY~B-IQE@|uodUNZ_C1oT zllJnDB5k;z>U^B>uFv!71`ri0F24-)%8Nh76r@RI$#v4 zc(!DwaO|;%^*_}f$Ds*mm8=^VPGICh8T6YFXRdr)pU6B*B#_|V({Oq_XTVKS38<(BABAx!vtgQYfQ zi0PRwjWr?avl)Snd~J7&npQ_~OBc=tT0D}K5zFV@uqj95h_1aHajs)W82**grE`HS z#yBsh0B>_#QgksX$B&0kc-B_Q(a{<7=?ZZW+>mX~WcqRit9dr|-=4aTm!I&|WTi7w zcr{R4PSmtpIjpoEV{lJ}NXhJH=X9n3?}aG7>QW3-oC|eHd7)7&XPOo2OQ=$i62oRx zx(Pcwgh@h$Czjv`XBEf=Zm6N`0dnW)9>;2_M#G(%KI5Jr&}$kUjI2~TJ!|6zLiuP{ zA=u;QCzggz9vjX=LpV>5)-47Dhxn7kVPV=Ds_Y{}=Z-G{C)8U# z1G8vRk4`^T_qaq}Ww}egQnBNoDJ`IEDs}x>mhwY%hqTY7Orr$IS=m!n!)@2wdmIph zhjf4;>vSsQGnn1}h@d6Kh!+yUz;v;yh*IfmxWXY<=v7^nLy^G_V_q|vQg^g6qE*6T zfJAQfbeIJF2`4?G18;V+|8$&V(N>@d>JJYfMw@|_>>CZVEtjWLrA3ru^-SLyC|;u0 z9yv9Zy80U&r~1aY)?zNB9aK6_Uod`}7KO)gTRyz034-nr9m_`aGb#r8cY>imnuY!K88w!5Z3G zZ~a_$86%o^R3|Ni(tgV{i?6q)H|b&xhtJ=jdhSA{LcLf$g{kx)o2?3Y@-P}4j+0tj zSuu3NdKHl&U!o=IGY>L=SGBJ^higtKC4cR^x8bv*E_3JBgEpSjcXLsr8mq{@?A!;> zHX7u1;G2$c9a5vHG>fOF`+K-jj7n=`s!WHSwxh}3)UH#(P8_&RDn{xD9N8{TH9#fm z&kIV=T{H`&*g@pGk5$Q*7LjTRmWHk`U^vne_T>62yV5d`g?1?ud%w;&kVpNqA3;Y1ws{Dk@7{o0N&g^D9q&9YA* zcAQJ2&;(NUjoi_98mAFJEONn;#8;PgqOOy$fCzguX1SR`i6Z9jRBjx5u9jxdGE-(^idZf<7J-eCGAC-Dp{JG1V15K zBLx4XfyL6)MB~3`89O>BRYDr+ad65})v@}F_*hGM%i{Bj5%+g>w9U*B*av0(w(%M0;yvEeEk`RBjT@d1qI&S}b#<|aOUs5ejdq41iITl2wX|5;E= z%AV6bw7A>PT+4ttzx!=m4mY}??%h>y+Dp`}4dL{)CA9{%>>(M25p{T1D>f;nC|C4s zbM&@?=%<-)SdMq*m?Ja9M_HuJ7LtB?-Lw%RMc}NUOsBX7hVx)K?~$Z6m-&A5jtX1K z@k`t=(A-|asP6>Yc-VL$SrJuWk+583#v)x#%9*jz4LDbK%SO7Xv)@) z1?aD!9#c7~R=g146{Qoe;SzaO!v$el!!^P-#{cvf6;^+!%pT^qyXjR+`7}kLfs( z0`KS4#=>rCIQfu76rGIV3eR~(H*c8O6ckR8j9g{HbjTe28jfA1qk(79-?h=a=Q6`@ z>m_0AMZ8=Mn>~Os4#EY+a&V>H8HQNy>{;{a5FIS>LRguO!(0L>W;h;q;%FY3Y6e=i zdsNZCqClZ(^#O9M`hfH)eEVT{H1;GLOEX%R2w)5t2x)Y7M$QoG>Q6hygyWp74wph3 z>KbSI(K!n`^Y}7`(lc|>xX!)9wqX$0Z4z=_sCG0^3Z{h$+o{434=Hap-pBcZA_^G$ zwTenI{EGctK(XH^{L>G%v}d^?t_5zW69`_*%r%25_$udV#(q-U$VDOWRbK8~2Q$?+ zG`W-n7~H2CC`@^Qlbx872$^#~Hg_9_@y%-O1cgJ?AY=z4fHn=ybvy*sd_N(^y;sXRA$l$C^$v1AU;Qd zbr$Jv{1wG%Ko!+7M$=f0MIIaS$GU{_OToeWFe9csi;maWPPe7^c3_=4K&!Dpj}XAR z-cA#BY{(1ZMp`LqIbQbuo^Y+xuiGHGCiLC|z5_=eb2cD3L=_F$?mQ+AVxKm4)n8oJ zZt+*}ChH2JlP*Nl)b9FGq2@>A4yn}INKh$qVRRpR19v3nPU%ScviP7(+bdD+XYBe~ zaG8EwHOc}L`&VvmyjoX1f;e4$8N0i46R?A1PzEb%ZqGR2slI7PS$B{tmF#rg3OVo& z{lmp7W{0)9;Ksn<|110a}Qec-+Iw(uV#zSiMGZ3&(% z%X+I0rk=uZeWyx4i6b7=TBh}xB^OSVixxS`yDKLW+Xl|zrnJDGHIm!y9Hwm3D0Lh0 z@p^`h6f>*1*Z2-DW6_)Z1Su&YD``7moU}7vgAw6%lqiIm65QUJF4H{x-Z0w?_Dvbj zE>kG9`N~8zR#iwBoODLF4K>a}W`|^@6&_dp#o8EFk1*ye{V~9dL22!X(QLSigr3`z zQw{lSNDW3kIK{V}$dL!W#jJYI1uGKSjB7}Pds0`XU>^7AHD3uvQIz-aWhTw?$Okl< z<=ro4U0?Hq)pLDj@iOOaNhOA&$DTLyNof5EfRdy(booI6(Ao%NBBS@7h_OhYnD<$` zmnzy85{^D=fv^98CiU=h{T8&WmyaZ7<-)^&hTT_@{FhnB(dT$Nh4&5hfC-C?H;i9s zjU-vq-^7gFSb3KDw@rtQCF*j!6skTpTzd?^97)2b7v^;iU9n*b12!uwT%>j(VZ62` zb!&)PjQE~JN3RVEfDDm5QA6Yv&=6%gUjH^ksKZ|`MXRn(o>jVhdRqY3p$-*mu`M5( zf`6q7amLGj+94!1nJG)5R4-YE*Rl^_Qt`VQ1oUK3o|d@HYu!L)1HB%bB%IPY8RG&BCTwu{4cL z0DCVl-*CIGHApUwM8|{`y7jT4a@%G1W$wp_b7M7T&jzTTRj#u=jcIs0X(>WvGsHPI zSCee?3X-RK%n!T4(p{}KQNxM9KkmT9fH1DmLCtwks8kiAj;Rk8V6|QITFh7}B)zIZ z&9;mIvtieGTy8I>A^V2<#A=x^f%VWnw$5k`9Z6e9rbfe@-Z98}KE@N{xz`Bo#4(j) zP&xRFc(7bwoD<7xAE651(CeqJjL7XO$C<_%Uk|KVnS#M&%vAma z6N~XRSvGhgN1~_XEx)yy#KgVX?;Z$aC9wqsYl|(u2n2OijRjKcyWEcBz$eldhSoI>vAN6ITjsqp1-V77U@kScL~G*EQM`pGTAu7HanOlZmbp?{&T8r4gC4E?Ip82c;Nz{o#qN z97pqc@Tv9Du6&Kq<;FWWAfdK7YW9Jr$+ zV*X?xas$ z(paFnF}&e6a%og*DCam{ii3e4kb#2EH7nX(WhXQHa=}h!7&4J=EuLl7f%+r9@u^<4 zhv$wki3ya+oZXCn8r~YC5GH!cg2ZDK08itiC zbt1l=Lz}`)oe_&K`Ij-Wg_n?gQDNs})8&aK9rKCf-lh5ZGP@x_0K8549Lu-$nLVFm zd5U^mPv1vgIS6y{9`K2l;A0o}49~bZz%3QYm!itI^3nUfqD)PWo$c|X$sFT8T${=A zFGQ~Iq-VG2*Gp4{G~pL*n~!sk?T@r8G-I#$6O)oo%a=1-?=2UWtnz;dF4LjszM?-*tI&TE;}lx*{X=RcbSHXG=-d zvDv)IAq;|(+^_U1hi4LfwS~A5%Oy!1QBf3g8x{058!=IO`9h|4r+x(=ar}WRuTJtI z|3H94<#WeYWe9!S3HzL~z+_y5uify)i{>Rx_UlO*E4x$eQCFnS9aS7>#NI)`GY0F! z=ZKSE5?>MHaP%|;YV(m<9~v|q&B3)sv7;hh@Iq)1%8si8AM^7Dd3L#+Kpjs|vB^ZGrA8IAx!w}oPQaC_p8^W3BRanD zcX?wvI|2RZ$7Q@VqmuHlS=j^T85kTaMjfky)vPXZ(VGO=-92&7#pr9M)~k8qCT1?v z_tN;?{eqd^ZiQ5Z7(w*3_D=j{O+qkr+RdO%l1|VgT4Cy`n<$ZDmIm%?CGqE0Aayf+ z=bRPL7<@ThcohTp8JK$DhLibnG4%=yzr7L(DKIVS%t=tDeMxl0k|BFX8XTAx%md{G zllo?w+@;MfXL>S?n4SGH15Bj3l|O#{8C&jaTgR~LUzR5)xrc;fZMkIoX_BvI8n=V7 z6<+!cl@c(&UNK=hgT?nc6B3v_tC^#^wKXxGTUs(`vkdj;tw1&~isSD~-CbJe(|CGGXO=}&=4H0Qe{n)a@+Gs3x9a>YoL7R~DB z+ULedSRPlkr0VmA!RiTyC%36IuAH&hR*e&b$XW%iDWbE5zCP}1wUliVy`XENm$Wb{ z=ly*g?=*3P$lf#Yv3xjO*;FNyUvh|Sa)SgFKB_nxGT$uOt^y_9 z)C4B7qI`_S*n|8KLoF+9+Tb?kV22vmx^j#*S8qfSbisI(@EVv7_!MXehnOOPnPu!` z$uwwG9?G^Ae_iZGW+2A&HgNl$Q7ro|f7$9gf`(M)3mv+_j^q{2=jDX`JnPRf&L}=H z56Us}B;|fet0-gPNmZC$YwM;piq@qZRYES~i8~Lo4vk+}dR3GwQj>f(I1k|Zw7QsZ z^t1TovU(3gVndH)EZlZ?7h?ErrP5hr4)%Bm$?-;{k@uT5e`rhFr4KThN}T(1J2K5W%AAMU za^NEtUUL(5EVJ%w*{GE<&7#JhL~9E@^+L;vPsy?ruO&N%MT;Fr`xR9qm^-7<(w<5D zz(CJRSqYQl6sMeAj{h;&Z#D~6TQD(hR=4MZ{*Kun9Vf}a98azcGWsR8>@Ap=rzzE( z49T{ExGTW40?4HZk1bj*Qc*t{aQ<%UFJ98Q`?-H3P9mRz=@}K@*U?Q0D23Id$K@kx z(kSI4#L(f$3hB6?!#^zGg;S@1vC{m6 zH~S^qh)?YqHxaV%oAXm=F=H3ST>L1BU9YWiq5dtYNo1xP-+4_8k}sxYmclMi(q&QV zJ)iyd37ytGygJNbF;eHWbO#M`6Qj|E=Y{-DnC0#BE%7$W)K&5c2PHm24+b1~uM@WC zthMR$9NABDcCe=Lb$QgzHG{HJz}okiFfhp&%1~Y4@n<)5tLtOWp4_*7b&+occVC&H_!OKi08C+525Y0xYh~XY_*mtg` z!wgyE21FP>v%nsW47URZf0r=q^V?R);~Ye1p}7aR6ibUB!@TeV=9zodsW3OWqgq5Q z_iHT^`*g1nCIF~}jkO!XBp2jp!@fQk@SNA(%)=|^*U^C+=gyc3Tdhq|E-Y^&oqfF) zmyabbIdS`C(OaF2WYPN!vzB^}d)lOtzh$gYcIEAYvI5dar2_qptGl=jFAwd$)ND+C zvY}gvZQ){A(Xguc23I!i+8*LpoXqSm%4d;4F!SxB%51}iV2YOA&{p5D6|a?{f1A*g zkZ&JOP`5ifXhyc!HA0_G0iX#RS?PJl$f31Tdj(Tty~(;1hDav>j|t1DzMTs$oJk6V znR62+)p1v?Nz}{xCQu}J(8v6+wB>r6F+fJ)@3;~v^)^&O7>dgcRXhFyNZS7XQ4;i8 z$UHYu?Xu+_p^a8CQv^fNMx3b7Q%~^Biy$Dx8kuw!s<^o+?63TZCYH@pW!2TdeJU)H z`OZ{S2S|k)@WHHPV~W`_;I4fN1$T;z3-HMIY#}qewgcD8qp*bTyyv zo?q50mVG!}%38*>sL7J7xYw(PrGBB67eTu?H*3atZB}Sc^aQyc8=3wP7YWh%o6we^ z?7`Kyy$>cb>|PKSn_7K5p{Aw!V+;Hp*Oy&c@paB|!7Rkd#sfkgamGJ6RNca-`*VF+ zjwZV8Nx=x{C4AUo$X~!O3zGm+bui)Y)*EN+P>VZgz4ZatTcD{8TZ+@R&G)tMLw2=3(^?TtOV!N3Vvg7dl+*(BN!0ejje&^07w|vNy>+Qr_qY?wh zc5tQ@910^W)0+W3*)=_E1@`d;KtM^#xKZyl;F?=|qpsgm-&Z#7NGPi5{T&@4$Nc9z z&Zv`KTI`iW#*lS4(kYtqIYEW>OH&9#v{G%{#R|%7@68V1+PlpTD*Y z1uuxl?a1RM&Yq;*qadl}aa2IwV}2-7ku&}bRWJSpYF6HhDU74waGvK_RH~s0pd^EE z%-kw%R&96m+^VPTu`$^Qv=2FZc53>Yr`bY*AIpXV$h9?SU&j{~PrKyAND+5PE9MsN zPb>CiBfkiaIPHp2ClcHL*iRSRTBWjFM+kdNQ`mUcQ23=8pAE4F|elBc$+!b9|nH;##*j#EU=3g3BR%wh^ zxtWbK?8mz`e}tQIleUj3;HlGN8Q8C|22Xjx%HLZdTjXr%FjL$ULTmG#y>J4U4Z#Q7 zjxpb@2*b9B8768;srja$>ZGw(yP7~e6ZY|^8tW7%0`Zs#gsXAll^|C&Q~i)z)oXjr z9cP%5s(1KE9bu<>%;@<}^bP3tGTY}?vqTh3*!B3anw$30cxVIJN^)aM@}7b0;v8MM zN&cfDKodBS9kl4WRA%#$r?I;FV=x*Tb~H(!uC%2AmtKZ!cy-yW^V@BQvtlO$0WZZD z!T%g8YwsK|h8zjpd!^oq#sh%N zWdJ5o%4^Gys(+ckebV_jxh9Od$v`ogz3iOUl=zJjBlM^QJ_m|g0yU;3gZ**^oUUsp(CW_3&~-PAGe(qql45a z&YDjm!A5`VKLsBh-5?YND}(47V>8C5xP)k_3%Je6tGy@H<|S+SdLj~;I;Wr2*!7J4 zJEdriFP^MXwz1!A#jsKJf**HE;($y#dbdovqny1R%F}$X`mK_$cuJ|pa#{;ZL3l+P zIhPi^YJKSAk0)qk3H1%RX`+~5b*rRL#-c`4Zr#w!$&|cJ{Zj*Ru8YQ+&DI3H$jobq zo=ttb6km+gCmQp}g=*8zj+HLB_vqv;*Pq^_5T#_&tyP+{i-t29k5p<3kwzgmZnFg)(5g9RA9@4pIE46IVO;1_Zk zvxxN{?}M{vCp7FSM(oOXcNN5O-ZS+qh0K1XnNb(-iG(oBNRrg{Gw4jojptyW$VPY! zGpevR3L<`#TKr>X6{uSFm4ut=Xj|EZ7Q~?6@BJ3Igoyk4yA+0 zE6AiBq^GX-wleCn7vrM&2Z%eA3EL(V9M6d9^5=ta=sb4kdud~spjATL$Tj5PgXYA#wFZ!R% z`9P%lV?i;kT+}#d@k*`I@<5u4)EpCRv@9Bl)t5X~AH9h+7l^u_w8GP0Ug&=ot!n#L?1NL-a}<1KsHPyj z>mXGIqO^ATK(`avrK05$Olr7XqM{eERsH^lDT+7U>@iUk-4mC3AMx2n86|&=`CW5y zkOG?eEG3>CL6*%k5m4M&+Da#?7p$S#S!dtY zC^6VlbY`2?#`h|TxZZ-JpAOAkk8roYkfgu51Ick+d3;sR3Yj7wgOASxnIq5^g`R73 zCVe2;U*e^)!!hcWJn{-{PL+$=l!T-;++I{~arGo|R)w!^hUR$vqLO5X;1d=y_~OUz z6Bv`4XFbD`)a80bR)1p)FE3RCOcydKsn7bUJ4z)X_m2eMUE7$Mqr zyW6@TdAV9a-0YM{%@HJJv*(u>5$g_&1zNKFK{FCrcI0e36CKK{BumI8Y6rnTdhyK! zuIUHjrns*2MB7}Yo`DzZj=Zl%yhv0Jzu4MItlYG5Zr~1X<@uUPAXv;>wK3WUr}07{ zN&8U5h5*tBx#-b?^3Z_I*T@-lVyiCzV099TL3+$>$tm4qG0TZk42bf51=%4PNW*l$ zw_T~I*+6F%FDz+p+yc1Yk=bW+jX1fA#fF7+X~pfS@i~tTJM?WPS-*4Bs25@AfIzWY zgWJ>-4!P)}=(7tM$|13+%9b#6(4NW$p2x4f=`(&sx=s)reQyBECou(Ppi zyb1W|JpBs)sa|E&U^fXAaY@9Hh%zEZ5eD#~iHA6W5ABJ52^Odzw@6$HR6FnKajcxz z#3$g_3G=G#HeWz&m_@i_y!16>o)vXJK=;ETo`!Mh zcmc5G@aVT;mKK!}IJbc#ap1_ZjJxvTdn$;q$&T_XIQPN8rc4dcwV5R~O;z^bN;>|% z>unsL_P6pOeAYxv);UtDaxUvzS>%-Ljp;jMV&0NyjWd)*d>(KNgj1oEGS&Qx3AD0)>OmT=}FK8yE|QW#DD6=I!#QxmJ)+_YD&&s zseRY1^3J4s?oNR~;UWf<(@<0GBI<8~xOL7OB|W4cp?Q{QAVyABB1T=)6CAxut*-EF zVjq8YXO;;g0OO+(ph;wK(1ca(#y?abh)w4 zI&2>0OL}v}j2oDemBZ0&($j??xwGw6KXG_dxoN_rjC!Ck8C5k@A-5 z@r=J@K@rQ)Plm#>U=6s%?tJtaVcVYy@RU0ir}F#k8n4yzdCca^`p)}$I}|>ej3>|z z5T3^8DC8tlu^v3-NKh!38R3~oMPj=4#9IUgi6lVLt|s-TsTmub@K2->Ho(okF?)}BmS(O&1(p&()_f*Up1ljW!u?oaQSB zjVc;Nx!<68$ycHGmDSf$yQ zQgUL7gi5@)7oO5}r&S$grlS(*e%0d`V9U@cw*d~gb=~@~26snYt5(^&HEO`K&akp9 ztA$98QJ=^zz}?z8CdNivDKB3W8;Kvs%Je6H>XK?KdsB-oae%oqlYGKDGqW;vp2)_> zqJ^D`S6LSo1nBvgBn_p&(Om;e4}+0+2LokQ*r7y$Q|~1hdER4|w|*d&OOO?{wcpRH z_@_`vJ68Hc2xB|_HAGnQ^Bp|K$)ltESItJBLr3e?ttqq_2xmjJ4OXIpL(E0rJvYVRH91g< z4CD(Bw)yh-IN|gw!O0x4vP`S_Nmfyx^!`!_F`oIKw!_yrK!UWmckXzD*0QN6=)P5UZm9k$=&1QhNKlHP4kX`Kzx_2p_MQf6=NJ*Z!vH!SQh4r+q{V;X($1X>^yK}#i`R3y?(uVl5T~JYC*Rn+}?G` zpY?ZeSrt!9+(=iy*{PepaGHH=NA9OnuY&2P!r6_f;+)`!CTNmTkWZ+a_5@lUqMm4CzCJ z5xxLUyUImx{>H|yX;0BT72$)O6tJdl8G9`RpMH;fJjEWG#>^c3zTq(xH$I1WR44+< zNHR+(X!mb|>&0xZAS@kFt49*vm7eRnJypda5&6jyD!#bFks+zkC7<3&ij?+OsT)I= z^jGux0rXF?bm~2Y^@9XCd;Oy6`7b+^f08)AK@8DiiLUra|PK_xymea?^0jtBrHy@Q@nj9l z(c2&1QRSJ}XVrAw^U7B?aEfFJ)Amv?d?cY8lhBDn74Z>fv+?&!GMU2}n#0CDd*q3F zO+c-bAf7X{6tBxwK3(ZdpXgAbRxFOkM`tRQr%Dz=5|TybL=tI*OJCh-mxrSC*v%CE zG5RBt_fPDKn*m#W?}wdeSrH=SpN0sactE}UtzHOxQDsGFm;LuShLIqBIt^A$M6`f! zel!F0SU$IIRXWf|ov^5V#7RhijCGmeJeEQTW;!mFXtIXr==h0Loq#JG$$=tnVgrGR z6Vk5GhJsE$0z(G>YZ&^PJGMm%7+C`LO(7zzx==;NKZm9Xd76N4%o*ZO)JRW^8g^x@ zV?`~yxI!bign+@C@j3e&mJyK3<5UJ6peV>Zl6SjR&Q%DSbbboYy~&3ITNFM5-%U}Y z$rgHRBDCJGt5I2lFfph*|J7JQoT2a<)zB6IhTQ&z&ValyUz!?xV4s83mvlc)ZPcD$ zM3n$b5Gx?Nl8IXK7`7~+<>{x}F`&h;pU9sSM|f9fzGy%(S;eM~CFjmOPWKosg-3|h+xNu#%=CFl?i+<&p{SRC-N(Jg@C+bh% zjAud1qS2VUW-f<8zMrq4YHNK79f!{WnR6MA8jhob@yDe`>zn!|_5pj(Q5SMSGF!ybMk`^|_CTH%@ zS0dm0IZiQL!ez_HIuK&`u@L%R&tO0;c%HN;wDH?7FvW?LHQVFE+E?1>dnK`6XuRRY z3LP1Im`Z4HSegzgbKs>aVDw1sPAcMG);vwoUUQIFw9t$(IldOl0)_u5{tC4KlL& zJ2#j=aVcr1$dx{EaRfRdz#(3zXmuug9Kpy&vtraFM2pR(AtVK7`hYp*C_r1})vVEF z@W76sy-%c)$U`?o1tx^LTW)K#1h`)TYAByhu~ZMg$dUqc(te4`71{)>Gt1WW7o)KI zf>KQPYm4*aM|%`#$^E>Ny2Ac%J`(%%uKao~Au)qY>P4Kus=P|3F8gxdcgYhH+AQUM zZT#0@KTo|)PGWz`-t6zN-w<0CmssD6|H1swOd_m6vG~g_MunF%Y6JdU6GLc&VdLY&aXuz2{?&kVFG7;v7CKCY-I0$weG~j

Kj>V82K<&*132J+X*GcV+`^#z3*Y3S$wWW{ev7FK!XEtt=L2;Zee^L zvSknmJP;2Yh*eF(!ttl+TlAuTL)idsfrB2fX*Lj!FA$NP4TwVtxWlUk09gR;8Se*H zW*|HwkTL^d4*gMPfcv;a01wS20_ru(?_RU~>GgeFB7leH5&<=ROVk4#a3%35a`5zi`gyy+e7LTpu5cioLZ2t`B#hH zMiTOc+jR^TH5fBd(baEmes1cLjga|E~tmiK?>CIE+#1*jjHfGip; zEWZhY0QV7z0B%wB9&o^!fb1C{sB9*XX1Sxv0o+F<0=UKTd%&ha(1o|g8fdBh8teOr zL;w#>Bm$Cf7T~f0NjMAJoyEM5NCZUt_ppl#MDPY|9wcTgfZtq#pfKdW7uP+=eg7*l zV+J+}3SB{q3&=nATf*-n5&HAFPLlcRBBpgI!0g`Ya zP~`8X?;{ccJT#FANWyQuBcO!;^^Sl7@ZWR&&_p6231HEa&p{Ybb>l)<$1DEyRu>L+K5fJO&gX&Q>;H-k26n2p9{NtqD$0P!B z)*rNK0BEW~*#scR8!-3!d%W*s5&_%->pq~T0BV{E=zf5j1~OsYHGQAWd}t~W(13#k z95mp61^hlL5x_%JiLd~N@zzrSMwWj)h5M*PK&*ccI@h44Z+&54(|^V6J}MCaFcf|8 zrf)S0u<5@8ejk+x;Gs7FKywW;T%fuBYq)@#?Y|AzLvH|png-1^sOi6R4GjP8Y5IX} zCMXRJa*sep1|AUs+t#<^J@4rHJU-}=!+4|6(zBQP@PX9HS_htV951swLWiSW&narSM2HTzO zxG(z;Os#>0{ln1N{oG{tc{t3mX0(!0MtPo%7Gk5(&d^2yzl09v~{X zgeWjT|Hq>IXI9{z81;XIvkp=(|F)I?dE5U7&ia1`r~kb9ew=mS5dTxK{_q1aasJP6 z&$AQyLg&~}o}VEe^S}B|OI!Y)!8X3y!0=I93RdTZ4=p%^XLCFv#%Z26x;Fg`PMy+= z`#G4boTJj{dbQw2}~;(T^_D4&0Rwfk{*XAm~scTxzv_g?8|KK|YRu zA3j`##IdpbiGDQRQ`r;4){9)?CYe_)f}h`P2|j)TD^AuT%GNFh{OieP9MSv?nmn8! z+U>GPQvl6|)ES%ozyqSzk?m)Qz+*fG90V-0a7vZVe0eNW5=BSKa(*)p8TEKrKUm2$ zjjtc*XrXfG(5F)0dz$t^oL1w+#W)##5eJ`*fkpmGp{+qtf|~|aNvQ4?~Q-z zKb#nMZhdX|wBzv9{W_1ktQ;x_Aamjgo5Z?^(}ya`S3rmzADYvx)t)RxP?VaXs}rGJO8HJf=o*xGxy{!y5__%U@xpHKMK5=Lw8{Kf3G< zK9XVE&1GO7$RcWu!S+0%F4^UP<#8@S@gDYQtAv#vUU}+yR9Vk8Ib>ni)>)rYc=3$6 z;+umrLh{9nZ~axTbiF#Hzx6woaXJ{R_M0EY8N{XYydmj-9ZgrT9lWK}8vISJyY?r( z-Sv2chuaO})laiG97bjR=hlKybc-*He^7F>sT_k*B=nzI>wXLa@3y!Je_zuj0j2ql z^YwK6_;-jzupsc||;yJSiEs>FT{`l50-wTTxIh<_H`lu6wL>%~73M0e< z8FnbB0)Nrz?ef^Ux2K%uLvgRV@V(`ERQ<+jaN6X+<+)V*-a6ATVu|)qT3EXqQf9o8 z-x$M#oe{<*J6cr0fYT_G=epT3RPGY@z@{9eZvEsrtlP!dnixen-j^jOP-#j#m&~KL zPOJ8`_jQQ`w6CHNb7rZ!C4H?FVK!h!hHoxA)OR+j3`%+~_Ur{IUY5lklzOuWJZHfw z3T32Hhl7d!yCFUX?`=OZ7tKVC2%Bg3a-XGRuV z6zS&Xlkm${lp6z|E0o#D5e0*87WeyBS|-m^hF)4IU*i2dGNG5##SEoJ=&CvbSr*Y) zTO-p0{!8GzLSN4}%HD2FzS`+-|K#0%b9~Znfg^F}qjL-LrZ@hrl6)Bv@7&;YX4+6w z!e8uovJ`}nIlGoKfbk?yNt~Vc?yf)mlIv=u02)rKtRw*Ji$#bk{4t`~pwjWd1 zexB^O)aWqWfFZiV6W?3jbDNC)w_SW-dY|8R{NMCGAj_x#3NZf%^gh2TkZxIGZf^te z#(W#dRU;$_W!#R`)hRmgB|Es2K8S~>}~2+;Xj<%|6lAt z_vD@b=|TSA*nw`J<=?CGKX3cLd#(2*od0?A{a)*B>iXX;{twxKes3~72s;oXDC}K{jGlh$MW&%Zk2zBKBn69vV7H>>r=$0tEwt%DC3j)oBFp$QK@fFk96G6PS~3+ zcdz{UwReC1U_W;}-#dGU#9!-u<9<<2!;jw+AUH$gL*(hje7XzvlO*ot#Jtu*W2x|Y zIguu!SE`Oe-M)Vl@Yg;P8<=hEyCH~aH53tpSC0tRNtQlY_(^ra+6c|38C zzWY~MvncYhJbLzX_I^{XG#J@pzXC95ZMxFSLOG5*44bxsY z{F%@&nO9tSqj$EEPlg-xX&*~#!e`tU| zmiW+I2jMsI)bl2#xj89Ncgi)e`s{Mu^X53{o!Y5Dl?nE$fa~f~;+7Xvzg=->Qzt+5 zRy?8Cqe| zX#Csa*teIMKN>}kQq@HZUrsq8yH%=Npu>{3CLq3mM)P zy4Yke?S%-m;nG>I+7GgpdnIF^8h8GGSUbz8xR!2R2MI30-95NF1a}DT?i$<@+%34f z2X}WTxO)ih?#^wJo&D{7PVOCN+><|~v$}iDRaLWU&Z@5Wsmvfq>O{GvIG_8{yjtFx zG1%&ajRilrgmZowdtQ7^TI*m%vJrUZle{o;Qt3%-Sr$5EI9jKr0Oq=>E^SIzPY?K} zjW7OXleFaU9&EMZPii2~S|CN0g&4;+XJ6P{=9G6BiiHFmwWGJR&Fmf-E#Tl7y}wVp zyR@0F4aKZxv9#XQvTV~bBVeav2)Ap<$HX_E6hQeyL3GANn)jM_me#oTS(C5w+I7kG zJACn))*jIO?ERU;ob#Brml+Xf3D4upLq^|%DQG=ok(oN9W}TcPTQ^-dR~PuA(-H0Ny0Z5K_b$DgRlG0=|hWfqJ1*m!Q|r7a9(BrkX# z9fnaK&Teei7+?fP9L3tosPn^|8g*Sj{ru!pwuVm1qdnI*+Y{0F)**{<=|7AyM6pvy z*(gYui{Z1>l=syN+Opp+OJT_-*QTl1{z?FaNT>AV4;?>%1jq3|mWx}PCh zbyg-lWMIaS(9(`~QKDKY_<-H{;gOG-KhuShG8Z*!$YWh8g9pIEX-g*8-a!;@SHGof z8!BYAjk&CIge{ zTqDR$iF?v9&U9OW4~x^HQkGvYwMU7UK?=d}1+rZ%|Oc;}xvz*n(ZOIi@~lu^&;@DwFho4(ECBeS*~o0|;d z>!PlM!4+IM^~I6e8#VKPP-YgRYJK+s>-(oTfbQ1i6J@WM-?uJ&y&I*JYBPucZOd89 z$yVvLoWt3{(0%>Xr6hQsIiA;Dk&@)OAL{1{>+s-)!7qe!Ll)Rt6T!pN{dTX-3c$8f zy$;XYSD^KRY*-TLcnX|VkaekF>GfhEl6V(+GCJ}fWv*g3OJ>+jjMp-Rg0$Kg-UUi( zgmxRvaR9^}ndsNn)Fwv^GC-?Jq6}%iq1_YZ=c7?33()j3v80hN+?!s6j}3I zIrjF8>ZwYtlseGhrNq7-44Lon zRTbFDF3;H-A}T-{j50VvIDz`;^V6}xy}5}s)#U42YqV!km1;WA9?OTOXXp4k$ZZ~1 z!cBo@4b@fb$+jo{wNnjfBhlEl3D@kAT40xcX1ymRgZ0Ib+~2Yr^9!@amFpB#vpnr!xbl6lkL@sl6(6En ze&X4>z`27cvAq6^@6$Bth1a(sJE!xeU{~xH$;{mE3?i_hZJqg0h(*FkhOxee%#N}s&J^M( zsTRSlDV^b|nlw<;dkkP2#mLW&u&768b<(5@@E|RusiyQEMEAczo4&u6;vvB*+#CpOo>wDOA*}4a8EE->ZyS*8fI-TS)h=P%!+r6!NlPoq&Q2J z#x!MAFi32{)s7@`ctZH}nm*?KQxD;HQx{?u-Coyz*)=7Gee7`K;JPQ;4qh{IZw7(uaA|L-5odtWLXzn?EpcB{N+&akJY>EQ849hh*Up z5V+2Pwkx5MldtLK@K(K|s4mWe0|C|C@r^b6N0J@qLo;EqVK82;RB>kx{f80U+ErbB zu!3{QW=HnPWeG0Um=?hkYg#d4dn)MF%YDC4Ln}pAr^>G`(yRvOEl>AbY}2*%aIYyR zt(h5tQ~W#<^r~~G_SMuOKdbH=Zf4n;(?ZvVU)l;-Vi(7wReg*i``jUG?Tgv=X)3qg z_Pj+k_*55rmHB)@#2i5>*`CygJBNJL67tZPR)fpNU~%HhM9nvzR!TW0RMe~D_!u&! zq;q|KCS5S`e%H*|*D^HSF{hgKMKD&_-@%|MLiIaAvpT`zIde|k9)<#j61)^-D?&+} z(}r8EJ6PMLime&QS$Z4U20@qJ5!KUoKnQQ*>tznto0R0ypx1rrwx9*P1{hYTYGg`G&^xOC$9R7-|IsH^;*m3dp-T^JuSoR2j z?)@6gR!7%*bKKXnV?q@gaOS;Y4be6&LBSoOD5q-) z>z9)&DB5+Fy_Jq3^iRk5Zbprf7#trk2EfIJd+OKUsZRH-H-47xxl~@-5{<(O(G33p z{Vqan?(9o~7z7?vr!-++UhZ)fY=P+%rh?{DsXap{)uZeCM_~EO+8WyUOSF2k;l5F_ zw>$2Jb_MEP_UCojM-#Y4qm zF)U1k%*044JpI;L7NxUOG$QVOL>9=Azvq4B_-ChuxUjhU?948RIAy&3DevOk_j*PJ z<2sgu#Nh0yaBG`vfs<8jay>YqvYHjJ!+ep@P2&9ERYB`U7DJt-nluUT5rt;jZ{6QjI+X_Ujug>WGSyQjBA{}FUUkbjIE^GJs(O!4^dR3)9 znO!Ns56yYr8PRlen0C}VUa7W1Q8{gGI=Zdjz*+3FHRppR5@|6oy3yh%cuU&2k>RVm zjBT`lyi38d5N;2kc~^ekQQ$`uo@L3r?sEd5JRqSFRbbS@O{r-b&EbPy1aLv-t>_AM zx(cp2uayJzpBj-r=!Q2i9E9<6YUK6F2$jl%9MvMpkBc zq?XYf*pXm9B+L~$2N3w|`h~c~GX}vh!-PB~jlN0bpZl=4OM-wHx%m zO`Oi28F*R$sUu9~lyItJFy1X-oQZ3_>y|?*nK%ISip@NvT;Sr(qe#>a%5dc;JPZ@OuAb~8OgImdu z8B)1hin=A!Hawd#e8&km^0T7Xkh3XE&Q&!w3mCMF#Ll*qrWy@?;n$M&BB?8dO5YT!a>3yetZkAK4J4Auf!AiW z-?X&VqxwmV`3F_Rz&m)#=4l~$kbU+pjGnx8<|VLo+O!H0%?~$wvo}<%drXaLtd?te z+}tqt6J<|{HU|r2)iPlO66=auT#O&rtG`>+0?rC>8VxO@wI}ARFKtJ)XYh66W8jiZWuO26 zi4-X$XGd@}e>KB&T3?zeZ`m?EvkuqM#j)+~bY|vo%Bw0&X?q{ZsK#AiqRMYRb5x5w zb$Z}Ynu}H$CyjLTc6#R1Q6HK|0Si9IcTCHp+R|^7oOVpm?7Ntm((JQ#uJAn(clMRU zDpwOELT-G=x)klWBJTDWALaX|(;gWO{2@S4Olph-63#|#S<^rI1yAuOVJ$a*XXe^f zY)_VdU@u$Ev_qS*nnMJ2thd>8m0sy>0-}(jW0a1WnlOK^D=3k6h7AAYvSRPKU_FU+ z2eIJwb@iqOznRJ)GfQL~+PlT|t3H9P8bsN56;WDfKc{j;7bx|PdZyFnMQfaTS|rhA z9S{+4(}c^(SCHuL2ONgL6kkIFM{S*-xZ=G+L{taHQA)4;p|=gI6Q?5B8E#VKuitgV zM4|PI)7_etlR#W&E+dEMY~qz}fzc3+r$wd*kI!d(gp7N|;w5Cdb@3Iww8i78h-nnO zs2uSaue7H+U-X%_^?|D<8=mF4mZjusx||6gyu5jYG5=P9c=2tols`u?9IhnfwbCZ+ zMU_H%^@+k~LYX-3_txh(#b-_z9%ULeq@7GatUg%0l}t{;tsqo=izTcWq-+}KlbL=E z`a}4&v;6Z5i{94bi(41HGs{q7c|g}kmV7svq{~~&R#Yvf%*U{#ruC+U42mLUm8zgg zTpc)W@VFr+8M@j+w>?(=w3e!LcMK`hTX74NM!7`(NpaVWhdUu2U!q0yx)Hi@7yGs8 zl{Z2Jz&799Ros6xX{1>@p~)uBHHVS}Pk*yS%bEsj@hsp~0R-mg2 z-i!i?8XW8ztMUX*@rQCAg%>qCYs`)p)ISBAhhs~uLTZHRK9oWT^OZAW`pI=!$1F}M z(S|VQlVab2s|qSx!_HI+gwA>6havAy4@T*XOJM5My>#s(Ljf-F6K6>qoBhJ8_3rGG za*T+s%2zWYup+CNM-|QHtv#&jDW*P(z2c=BeBz52F&}|kai!~3kmHKz$9uOIDgu|H zDUCpBb5LnavCJnC??5r;>&Zwg5Ps&J@98X8mEJOfQ3#b24Lpc!q#<{c@Cw}G7c`(d z)lU}E;MVQzC}@1>DQTR{`n*L{pg-uKav&_(qm2-AV%w*H$)R{uqE~R5D_pV3Qa2!7 zlZ?rW(pq#~8yk)@7h^>^aeW4hKEri-Aesno9a*c}J{`a^31!_w=HZz&X| z_W0|auf9D5#SztD2c7f|4s*w{^vsASyRaY|y;fG%iaX?wLNuF7u#nzbf6v{`+-h0W zD@9h{kdW4>btgw7RDDsBALPzXdUsJ$reFh(_bl48%C1mjGhRg^37rQvOJHvz{NW<4 z<1=6aRI=h#@5$Z~Ft7vXB*(b(EkN%4jxA?S!hV(*tkpQ|R{E00XtNLa;ZM zRa!?^3xu`bq^gERUxpRo_%s4sX2dT zy|u%hRmgdC|_N_okICeVV7ovp~lT|`s=aGEp<#w>pRi7gn z`=izlzA#1vf1ZWsR_rarnPDpm%iy}upnV%86?EHmccBc&U2?fQeif=#(z44Y-9bR} z<@)$5_Q3h}z*2|a#pby9^<1PeALs|n93v-eov9fRm~lNv#6+JviZWFc@ z`$s%Em+zgPq>?@)5E96PpxKd~wow45!iWDB^8A5*HE%JD?*r;@Oy!`mAJmZOzLXxlz|aEo=!KyDxcq{_5!) zl9mp~1M~eYP|hpL_G0Y^@k@MjK29vTt|J?83<75uDV{}(0@!m6{JeKi+|+yf6SKiW zEj4QE?By!Oz*?qP=Y^k=2fvxKIM*}Bt`i;0GaHkW0mllwX?7%dnYPgUoVJjYGQ>3; z+&S?3SJLGtf1KZ7Uay(+5{aGC^Jpdr=jR(sS~W8Q(wlY(L|YqSieYFY9A9iH{`SGD z)RNcuUWJzRqPHsucS=z!oa@xQ8j4V^Bw{wF-gXr*Z}E2lvJv3Ip1XN7><2^k-k4!d zAo!%FW3QbfK6B8x(6WuLuxHH*cNv9%|4ju_l@>l)_h17%FR4IwLZlDXF+`e1prdCK zfu=Jh;K+p!K3?5xn{RwG{oE z2)HNaVek(7tacll>kkGy>nPaJ<0z{d5S7qVYxBV7Bx8185u2Nbr%l8bRnX(c zWFY33XggluNV%_kynJ*Xhg7(14X(}`h{Lfxd=UjW>@bd8it_UU`xIZNs_mE5;FiVa z5|(01HimQh#jCl&B~tUCHs9^pxu|UTY`4;K$h>!>iACA;7obyA{fK(D)`>mM$@)sv zA2UEGKIqbM!+Oi(V=TyCtec@4F( z*(Z-XN9eF>A%{KI9@dZt{+NP!?l|#!W)fwmqtd?VsQmn<-%vfSd%;0L z9`Vc_|cB@>IqUIv3Cr71CmGb$b zZt%UKax45Ou+Y1}2H=rJ!m25Q|5f|Uc1jDJoeBN{V8}QMf+l>pXN)oMQAr`Eq9i1R zA$<-wsloeY^|-T4rtuVc*hy$^645Sxw9(SfK1&lTU}sMSdx#Cvqdn`46+OS705G~Im#*+|u6mqno|J)$##A4Iksddj zH~4y3FS>HK8B?UMMJJ_?$RG+BFcDk_wt7u4gyyZ|>z1~k=$B2~;<0}+bI^a&?v*g| zz)ufk6Ugr$+iTfncP-VTvXxtl^D(i?tvFsC2^cr9O5Oz)C>)+^@Nr0wRm*Ce^>bO5 zR;jCLbXyT|T|!$6@v-WsI#o}5iFXUa$C5i_zV7a-o-aqwN&w{@Q}?Uf5&IlOAG5I8 zJ|Kw0><$?^8xXO!DK?d4$eXv+%&Z%c`W41fSjlL_kzY5`_LOL|V6AT4R?YX@_s?5^ zT0p3?#cvF%MCB8>g{hf)oMGIPY11MU8P{66bIGazaE$dM_Kh&6+2JbLgpe7ok5)0UsA2{6GxMY4!&VZxgDr1lxTOJzfy z*Bo`y76_$qiLH6TAbivTEkEdj=l-y1n}qojLW*41^?cp3fwp>Fe}T8iVZKEyOyh3j z&dFLjV6E7HIzOJIqcWbY-MvMDWM*N||9ZZ|F{z_9rkJ`j-gA$d_Q1`tzQvaCn=I4D z_Z{k6R_gnF1@-)X(7_D9cbJibG$4p`vK!MYzU@Z(Kw8UNm4i8!R9=z*M%+Al!SG*R z+;Q9)t6(HqWm98tMYi&Eh{bSrL4*u9kvM2QT3QDfMjT#-k>VQMS@42{vk`@vy#bHP z2PK26?5(=qsF_V);T|gqQJm3-Z4OtA{hey}GFkIs9v(!PgQVgp#$cZ&;+;#c=%(`jQ9cH5U zatDE%tCI_}B~&br73E;r`aVA+Pwi7=7Z_aH_T7imw-W5Foo!a=-(#V(71$Ea$G+j~ z@$FYw)A8dN1fp!%XkQkiZ0y3)1J!*ltN6kxH9NF2({#PH+<$pu+y2cpgn1;SB6ndw z+1#KS)uHUVmK~?Grj9?#w7un{`WyEzL3IsvSmv}e+io4NM>Y6bA-J3~Q8orwBK*IV zrD9bK8?L*|1)6TNGmqx%O|CYciiRdV&lzGpQVItv9Sgl5eAiR+8UK3B{$oV2`??+D zYg9*Zkmm5%;Cwqqx`LHtI@{!kj~u?B02k;y&9N+AP1`jlwzYY_8bWgz1SUA2zH%J<~C?i-77A;FZqLI=+YMLzBV7p~03R8(gnpM;`o+YKPr{ zOySL3PoLyVP9Ty+)iJoJ;gM1umfH^b=o_K)nWX;ZtZ`(WQ2ei(@={nmY&dWs0)MNk|yGwSB%+H$oi4HJAKorzI>*s6gv<2+l&Ph003PP^=@=W3rV zm2&Q2G>qmWg>2!FV!TKeFZg90o0h+h55 z$r;J8+lWFX1$J zH_h+qT58=bD?ZS~B1uGz1$j*~ebHfzHh0WZ!{s-FGyK*?Kaauo`YW4ek@MRD1%_BU z@^4uRrDlFepHEWB_7R&S3Y8ix-_pebe2VfTgK%L@yt&N4+fx zI%+VHK4b*UBN2CDK~%9}KWAd`XtOmNO@aIc*P_E6384^kmc}tBT=*o?K9>xx55Rzx z+*ZhTHYr{Ea*y6vI4-aFI)}0+K_{SmGB%u>g4$9zE?WC6tkB}T>_L3hN12W3&6}u% z1}N#bu}}y4Y(AMYkn1kJR2^)X+d0{m9g=op{X`KYtv_d|7;J@!Cn)*Ay1b+44E7Hg z2&#Z3v9-^BTGQR@jsDU#mr9G{eyu8#_L&m&<<#Scn~w|ZtXlm`Z1ERG3hI;KXKrQ^ z&`sTJC%IJ%ujnml0@l>@I<*U7*P zxCs;tH;l!Y5&;XIBB+?Mo$G~G(ryk@v$FXGnLHzO@)Bt2E+av9u<3WM{>-5j{esk> zi*^Fv=qTuPssp|I@r5b6S4;qt2&xJ4Kz7ltPO(MwOeug@`(SMSHT6et^bFN?204?- z#<%`^>MgQ%yS++D`LmBaqB4(vb{vauTC6J^-95F<>uV_0E?E2QF4GGdj_sASK4AxH z_SBtHL8i9L{wlT7H$iX0R>~Gs&K*IQlThgMJKzv(uO$}0edCv(9FZfR5MD}3!)Wn_ zKl-pnPQZdC4G(;`&^(~F+oCG4MeSs-scp(q zNuH5wHoy@q$cvbua$$l{Fo%xtp9G8IqGZ&g5YWCRAq`RX-@^aq zA6aW7BDh1A<4k4pbKUQCU>OWa?@25HGM9@XdDyQMA9tQ>6fdyhuT+N;KB*NZm}2ze zQWAK%w0I^7Op5Gvo{e0l5$-I^&>_=~%!-5XYCiCA`0Nvbp>Ldp5KJB-e5#HSg>>Bz zdt#wPgwga?Eky_Oq=>J{uRaVhT7#KdWj0AgywF~d1SpjFnYh6g@RF{UnYgpY_;5j4 z>D^u|aEn?XsgL^iy_P88)Mev@{C7wX#m=@f1%S#c5}y?ZwpWFwt*Ad19Ajb~A=+>` z;jNiD7Az;16+mO6u(d7uvLugvF9UAfcqng<^X;m#6npp}L!e2{aF0TRy*FGND4T~q z2G>Cvee~QAasmc;f<^cO0C6{n{51#4PJ`5+RKP!2|3p9}!^^*l52$#j;XLnSbabMEJ(bKrbl*T^H6q2xN^9Fi z;&#b&d?GBr6Qq~uMOBS@Gmc95nL%Gyn;<6-@Vj7U-NRpb{+KOMqw`W$w!BRX$}HvU z#wlqsJXh)PaBl7qhevV1B|IN9V^yu=-I+^Ox_v4;N3#;+ZnYW~qk*7hjAOTvuy}gb z5tK*FniiEmD<%%GzCY<+#t1DDXcg7Br@;9Ke! z^8g6B20p#FL_fo8hS5kTS>#ZHe?!g=AXv$R_Wu>ExW8wc{k;bE5%F3xgt~9+B6XDn z;NTZ<`7*pUwS)O}$LXU?UW^UvQwvu$U;{(uu*k&tQ7~rHkY8hL_20&DR6sz!C*{`}tK0ce6T;8DV`{~jehRTz!*oNN zH0fjY1q=+%6ryt6Zn->G%~TFng&6CG-DrC~Uhjzr@K%NB_8c9xIk4*p*oOglfsOb` zBOj}@=uEf9NYV$WG>YwD#-1)hDN6EkI-1P%=eMS&<-ufr4+N%S=jFR;?J~W7`EiuA z2?KBoD9d!F9cMSzSMReV#VMARyYPDNT<}>;y{QNoGU0@yU-1-#SPZ^O7V1o zdgv~|m2GbKThBoHSh|A0-`D-8@oo$NLdOqnGC<}J*x37191IZk{oK_2&uzIc^5MUC z<^B<53}7$zi)m(nWb7ZNe7 z!VA;uZ^#1E?+9-%sH#8Oxn6ivKTN-H9)4j&n0`liWBQ>5`h%r@X=?_6FzBVN0o;MV z`t|P!Z%hCJ-XAUXi)s3oJ`FGp;G+IA{W~}NmqPkaP?iC1M*o86c|l9C{B8PoZuX}M zZ!d24BKv=Fv%h8kFY)BRk7j?G@b=3LD+83jdv({b|D6 zOR4Drre8|^57WPMvp-FE`+-h;$xr~54(Q*1l^PJl|GS6(X~Nr!=@$*+i)lav`(^rf zLGw3CyZ@DkzvRI$L6aUJUHnhb{2k$q={JJA|CQ;NfcbLM7e(ZMQiY%T#D6OxnSQ7` z|6us|fmj2mKVM4ypE3M*gg1Z)^B;NP{|8Crr)c%RiZFjP?Y~8sKSaC#MTGf3A&LC6 zt^PT8`Az@$r#|xks>ytLw0~FifBoA3LzDU6r^f&M^6xd706x=8C;Mgd7fFPUf$o2% zkt|6Vw8CQlNfNP6RP{BgxY6i!gh7WOt;K)rqyj^SiQ4@1SdJHSxnk+qZ$iXZ8UAHb z(P9399*(!WYd-{W^f~z2#+mCgNXYuAjkTP*&BF(5QAm)-p6H2`O@DP_a9*k!1 zn&#dVcOI*YGKnZT?rr5KVh2Aeitn8>*j=7LR|H8EW;r&YVO5(@0H@;)6T8fwE8M`UJGEwzQ*yYD-Kv*%Or-Bd&X%?yvDpot)l@)QmrOR zR$kUy2=VPfE>`J$;#`)lslOP=YK4xv*-YXlr=(e*90?a|eplkbqYw$#y8RbX%>_y^ zW;l)VM9rfB%a(=ntXWX?nfoPFE(6WDjELphm0SE|>ykQZ=e#F1{mk4)IAsMgdnj*w*EedMKI?k${hoI9a3I#SW=A0OXw!Xl}!ktz?L7N z%_YkH8p`}?`drm3&om4TFm?|QV4zObXsLXIm+f+L(L`x0%j-T-xbDZ8{uzVgL$-fj%~)ix2=UN|Z(wrIWA{|5IiNGe&nNY^B46@v@s+2{nYSV|jEcAQZz zDcKYmD02(5{Y~_g%}qDEs|w#?0k2b<@z!(DEk(9W@Y3<_3if@C}`!3w>H1_ zv%(yI-~`yG22`U)jnaA)_O1`ipSVAF-LpNwSKig<{B~*ZN5uX$nEXt-|1k35B_IHF z`~N~d{7gq*BKQ9Q`M~sJ^%8K4pKFOfUc97_KVJMrGWfab^>XDGs^RC7(2py>KKx_3 z{cz$sn`ig-NBDbLNc^N@5gqh$``XC%Gl4oA z&rguPH5P-*^LE4-d}p)yh55;f7Pdxr9pC0TLoW@>xHLbYhCDb#1DFS4jW5iD9EhVM zkLA|euVu@R`^tDvH-{g1K6%`1ogR(yt~`f7yFDAG=BVX7Z0P$e4plCs#XXSj`$N^FNl{|)`ylc7 z)7@^ji*^@p*$*KNCD0yyZ?)+pb$%tbo<7b8@u2AmF4^1!Ud!9i)<@j*Jh{l4Uf|K# z^h_z!q`j1`Xa&RcnC#-Sn`f_*nd~4YiWa^=t@xFVMAAC$mNNS4M&ctyR7Kp!1I8A) z{Bfy^V@<1px3QnqzQj!G1hEFQzm2|P8^ENE^5?D39uiL#78!3_{H#_axBJCUe7FdO z*5o7hW+o|mqTDKZlU#mb=aH>~Qi1&xf9^)AoU9;;ylf)bLLPP<@Y>d~2J+WC*;FS= zx9*y(i6tfTg6t?1S?}XIaxlA8uRb4&hZxh355BKe2AKBFHy1#S%?w3S$IMHzSxiZf zL%T51>a$5Fgn8$`4B?=Y8jS6J8AhXg?2Z|*GBdB4lUTtfIb>$#m{H)|a z4-160kBb0P#+TJ9??-q^kEwg**euR7y!Rgm~f zub$ zz_{riwg%z#k32!}Y0YJfXKrFJ((g_`x=I!uure%|eT=)Y{QUYo+1ho{nQ&(}Zbs>O z^Xk#95K-VE^4<9wvKh~lC1qJs_pv(Hvtiiu2>&|uv_*BdB2}|LJ#;uKmBI{bp+%xr zkFv;W6cvcKSoCN1^Cw#Q0Z$R+QRN5fr?tg{>K%oom>Zi|#1RN{Me?xwF{s7#nUmLA zo#beIo`GUbMBnd+Sa(zOhP!Vogt*j+g1c2?iC8@O%bgqKA$ym0ytV5TEa4)ZN+uQy zQ_g$f081Q48W(jcHHMZg_*Cn7d3ioYB6&m?M zlAUDew`6LxECz>-BRvooFjL|on$s3WPoH*Z*T+M(pS{(P6E_SX?mELXhn#pH+@NqD z-c@M7Bf)Y%yOEkv4Zx0kwq&9&{%#j@i>KCRQKL*g^_k%A^jOsdroeST7;E2GmwF67 zUlUWX{Vwbo`}qP)5bJfT-*j=`sluIof9g1vwwnlb>SkD`!aCcho2RbX?_APW*ECGx zIjpWn{eD%W^fzP+m6_O~6b}vyO#R#Xy-79^9=AP>g+L+kO#41ymlu)W(b_DhL(8t< zISt$2Fz(AfjbS=MJ1J0yGKKBa<@+ix!-jA|b_oe|ld0KxIr+!45@N20_^k=54-OLR zA`%N#VB?vZyM$r62KX0BsP>Q(+K)S7DwW63 zsJ?hyr%!tB1=Lf@Y8BfMg!u$lxGtv8XP!~GQA*IQ1vo-PWR6`iV`N>bfp2;cN?Zoe z!+PT7S8N@q5=XKwd9M%#XGw!u6}M_n;=vuE52KeLw{F?^B-z)AO8q&Bos1y-nC=qa zROW?80DimA0VN}(8MzZ_&=H1GYQ9lUJ@WLA!+s?p;6c&i`3rrf=Ho8tgX_szf-!UvOPjgxM2Q+;x6)Jb?m@I7ZpFAE31PF`pr9^gbHIIKt~2Q^n{>U22-O9>`0O0xcMUcDx%*2Sr7;?S0)9<=CH>PG2PMk+9a*E}jP`U$g7p)8>PWr zbU1)N8MwfW{?1?eUs*0u*~zK;t2+tq~ei1Un` zO&ZGk+D8(XfP&_k(iEo%SIpOxef2~G_VwtYlGwA|AbY=fVv$}#o5n{@7rcpq{z@Nm zwMs83?|SKDlB)Ii1sxZvh(oLZJx;Fi6(dTUKvw{F#B|D6^^U~y4{zES?ER6OXt)GU zjX!oWp(+D2#jt-0V4n@rd;>cflL0P#;FCE869P0YA>LCSGXY*yfPt%Kb->YBx5hk; zyMg%5hXoHqt1IyF1e2P`KKah@G|3e`Zp*UUJf?Dg&W8#ptt zvOzxA$ZMFgk8B+KSvd|KL@&TJ>fSDcH|oNiFTcvZoZrOv2VCjq8H3eN;0r(Uu^%~Z zugr-LjKYZ@vjk2!T~tnzxt^M$yY#hUVP4=6qO#pB9E07p*4!cOpkvDGRztKR-ns0T zI%;EM&9;8M??}X|DhnoLpoS-D*n9L|WlBh}rz~b1ycST=m{0p&3me2oo(mRO0|<#Z zMOiF4T@JEXDOs^GBxczlX%W$dU^fgaOUE>+vO{YDVy|Z`Nya_EU7suuPsrVBmk#x-wT^qqgky{G<8Hh%$IkK zI|fry_a#2GWDHv+xu~4nwd$NS;E@SCb^E6?1M|iOS>Z)27hH+`!En(yqMym-A=fcdw14;l4!{>a1pGgI;qYI+F!SOI-oJd|#hdtE zd_nr3zVPxeKYih~ayrBl!2J_6RZd?j;aE{`WBco4_CJ&LXe^tHN%7Xy#*bDqFLIxW=qsdzJ)kBZM@3q|e zq6x4Fw7M)Ixhd!JWw1X6fz!bF2@=ae%MN2Y5{cE0Q90Mw9wR5J*hZ&ObuPFc6GG&8 zhqj_(a1+A1vI~o8BjFlnttgGwPw?wQAGT zzBnigStY;${_J$B`PXcGlvq?5+{OK{*Tu%_$b86d70(chOv2=CvaK6CO~_E#Nwr4^ z2-pMluL_INCeEhS2)5yn)1&G^Y^yBE!D)5!THo3VaA=@4@NqY%Y8IG)i1sYD&Xzvy z@avCQN@kr8(R^l z(KgY$;syR7A0xm=tkBKxXhyr;4N;o#t)R=jVO1*`P2r2IQ`{>j)D)WOt7FThxQ0RV zC5wo3bjO#>U=9|L8R)WV@e@G0*HVUTV>s90Hl4n0XTvJB%zFgHhHgM?@PCO72YIG- z@sNi{b#9~Rh>il?(sRQuyq61AO>Y)?n!<6z4@Buvv3y2#09mcKj2y@7HoY4vIG-zl zJSHSPT_eWF@?;4)R~=R@A>Hs}W5i{z8YEDFj*lyCV=I-TCk}N((yL`|*gey2B3YM} zYWrS?6)-H|0)_=FobCu+RzSCD0t^dV;~p6!H#p8o_EGfP%KbZvT$KLz;upKiNtpR* zbHjQ5izKi$BU(s}1SqA!OBt9zdMY~92z5xsT475YG@*7~UAE>g;ry&k!uZFMJ4W3XZ6x_dv6YV*9k3KE-Be1`&FSxEvLh@5wp#J9TkAbN zAMnJIJ7{c&?MXj|lM%2Xu@(sGO5A)~0S&kbB1}i18-8q(E^6)SU>z+jjr z__fjssRgjr*$ff5Rp19X)jFNWU-&p0+nB1Gb~cfL$r0BQS9 z2TIkT!yN4_2rF&D7z^MdCVV~%Pvc?VnVeSH1K5DfhIqDlAeTt7auKQKO6tatkAKq?-P>g&zHQPYT|M7rjr3eM5> zV>Nxmj+0~v9*ZVP<=3YFgzG`F8k(4EgPxbg#ml!UZD4irV@70_6sZsp8iesW(xl3v zGv^2!t3V?6^Bl}(z{w5uIaL)l={aei$Vrc3?-Ga%P0jPfXw&Sum6VbCc9`&$`&BX^ zzl6N`7M_*U)G7R7hCnUzM5TxF5Znv5+;eu5i;lWisV#@+9 zjh>(<48<}<3J#(bOHtDUg-RY*vzt7UjiWN^_br8o`O_l9-gH?oX58i0f*=X{Nu{31O%u-=1sdC#IQY_9s%+|N|^WK~H`r>k>Ju*+I=^vwFF)x^Se?U!&%_cjR! z7~dz2Z=j4$GNRp?BgB;wJ$Q9K8i}v4>vgAAvAB$EIO>A3NBcHC+*5se{zIfCsab(9Tptij2pa)x zT1aQ$Ei}K@$~i6LC^OzkO9np{?+UaWu{n}uAZ>>i#%Y@x?9Q=T_j3YZt%(VtZ6S*1 zb`qM0>!ioxfhvD{r`^fMLGH>>M8rKgB^-{BrslYl%U2s&MX~iMPLimAj{1FQ3@#-6QbeS` z{~QsiC_F#sw!Ka{;c4U^%O0;DW< zWtKZP>S-R0*b(Xxj$peAPHiVDX1O(EZ)4KQ$(uWbMASbiyz4$wpBT>ivCSiCr`LU9uJLM+ndr#f$t1&^O?Uesw+Y*vp*D_DDtU0P~k zsL)OsYkAAfh>%!uabp*5v8nqN&zLqEt(%}0%GI_AX%H)ql|)(!n;LW$>#5lP?J>yq z|6%Sc;Hv7Heh&g7E!`n4DRJm9ND&dFQ@S~H!x0gXE&*wz8_7c>AxJk!H%LlI*WLI$ z#_Pj(-}k$p_jj-Uv(M5 z#Ia;v>($3c8Ldh6*Ni8|ZO+lGZ*Z+5`?X{HnLPGWz(FFx`C9Hn8}8G~aGQF*=k`;| zAEOpe0!DpfAPL)VG!Ytg7NOcTS@HH7*Ds?+d4^k#QeYk$D4yx8YNVlQ^%%p6mJ~ zd{FV2YjQZzPk_>o_|5FckBZnIyg7d@*|~pOvYymwy4iw`mrN9pNy}b^Sl~G0p!EDW z*40hs!F)yl^9ePT)@lfT$`;++YJ23FT%BmSGE`ym%zef&!C^IIHTOOy{llf5RVNbt zLZi^hoOuc2irS%~s?H22zaT|SDvk)*Nt>c%eT=Qx21gs(PjN4$S|e^Gj_I$VFC?+Z zM&Ewb_h2RrTX0%i-{mRsP;Y zCmCGmE7|f`5?*wv2(tL~u~^l@!wylipR!T+6o!*%4JT~gG-PSd4)Ep)D8~v3c(_O+ zmdV;a&T8OMKRmn9b`l3e&1RkFjc0h%VK#3OTAO&!Avc)9WM*Hfb+!2uyg<62@;461 zXpczo-Zxxh@pkgzMHq8NL{~?9jrPd)VOlKj7-2{z%90Vf2MNx z6N*pWA|o?0?b#i!eLQ%PuFM&TfFA%ZQXX5``{;J+8c!Iy-_)*tlh}yh_0zP3$PBih6L1tLa$Za#6T3ze+8UD%9hB zv%_cDxw#@S3XbWT@FEdc<3y8QS;bCDuA{FT~@9U!c7{#t9 zD#(K_;yY52qUcH5!-()9j}F5CKE!DLETr%8N0VDzJ3Y7@HQ%RtX5-beaJX*k^E-RC zgKrjrLD+I6Rbui4mKstfvLb!PFYA^YKXG;d+me5}r!~Pvr4{>le42O)d&!=r)8Y*@ zP)=R2Q#k~T9^1+wi!D)TdS`U?eM(CXf>=IXa60wN9&5XTxXh!HP`0vRit?j}U zxc)LbDYb<;$>ViG#8Ia^VA{d(+je|+VOjRUV^cV@IiooDBV^&1Gq9`&Ub{%YQ4YTY z(>Z4s-)Ov3xLI#4C&}Z6p7N5r^@Nj@eAT}daqElS?w9vewlxR|-J6g;agUIW!aC<$ zno}cq#%=i$a>g}Aq9!^CkBv5Kjt=m*J*Zp~PA5;PNIkrVA?iFIYRs^PR?KwRzY1ohnyMHw#;G%$*%WSdfAe{=)M6-QT5GJL*3>p&Y-yv#UatCWKF`Zj{1Sut zJkvL?k$mV@Q>5iEMC$vBYMA^yq=b+K&g!Mi9c2vQ9dn>AQbXlnxksC8CaF27`5kZc zNW7VdkSqfF4IKSPdhW4Cb{o{wh!j5vifi7R+yRc*EMvgUe&wpT7W3D(4$^JWJ=)Cl zD+zTj@1iCK?&|Z9@-7#PYjYRsD-TGqNWdBE=&yWsEzwt=Kl&{E^kZIJXXJoz__*Kq3XVQho^^>hrWafmmi74U9Bf?E&2>?v}H@r zB$p><__NRA7&5kO!he3&K2#I!fd~$ z2j;NMHSW>q)56Xr|J)C5>Z5=h2<#HsD=t2^)k|VZI^=NKi>AYW7(c`*hLguaX~1c{ zd@u!Wy5GJ!K*cI`dxDGTlT3U3?Ayf_MfKguX{e|>Zd9+ugQT&fca{|eum!yGI?O#b> zm9G7#Ti9{|O-n9GgD*41e@^&S>Dqs~g)JE9@OVj@`w|)Olk`>T+JCx-Ee`<0yd=%T z`774|F5{nK_NRN;asx?Cm%RwNFWVJ^|87^z1z@hOAbpu4^{=Tu|06MD2VjAmK>iRr zkUI@zjQ_1%fVf{l`ZCYz50d7%)N6n=$G0T)AEd7mvp?O#_AeL)u+Eo2qn}(~C1!uT zhwY^m1Gwe_8jAiTeN_)zu0P$w7JO;N0Mgt5DE1f9SBcpl?_qms#Q@SgKsw40SZ zpQNwqVaxTWd)RUTAhk=|5umKWI@d+rD3tzHIyc3+bz*>`ynbE2Inz zcrt&K^re7blD-u1Ur1kJ%r2`T{8`dKS%u5q>)Zg8>qqZ(@Kr?)z<;{P0nepU0iGET z07B;A{9C!eR~0z`|LGzJ+<+c&02<2;kO#Uy|K*v10j%&pJTvg0E^@#Q9A+-pnjPr> z{d29^uM)FAUE~1p5ib1%c7XXGeggQa8VBG%UE|;zW)dKMi7@y{`l=cSU_dDTsDN_- z?%(BnUmibx&i7R{4#3~Q7k_fg1R(C06AtDEe3b8=@MRs7e@fY(u5rN40jTq(Mfn#K z4qUu~^dB~6-@vH=*BqBl;ZLrwGG>3e#=&I~4}fXlMDuf{uc~kW{?io>E>W?Ul({ag z>32T?d{u=5@Sm=5z{o!&A0Z8JeYsiF z)Z|~mQI}4^@8GEa1djX-Y6PIVE+JjN!n(c@{#Q%*D{kq(#7g}N4gL$c^!wNTH(05E zdN#j)`D&~bFvY*_7eC<0oIL*-cqu)m!*cu%07t&|6;mxEAS?bB9}Uccm@Ikl0txnzxykM8Lu3E`_#r!HS6cfzjdNS7RUF=_a#jcmBNuvd7Sq;Qr4$ zJyyp&Lb~F5ffSq3>H?sal>(AHu-<*b$3W}Wc|FpdS_*f^gPpIdySsPX2p@}_U<9g4 za=MC&T%Z`zJ>ZSlweUdDh~1oj1}A|-$T21AU`<;nsJ<}DdtZ&YnX`{YlxKnVi0F;; z9e&vn>9bS8w;ibYYhp$QwaNVf3!=c!>WIR<7mwMl)2|xf4(_QiQh?Z}43Pn6g1KX{SP*ud&6_fC=4iC%lHEO06>XxK3?d4zMniYh(s2^qECq!(*V zvqpO-{w8bvFu%VSp?ptb|Dk@T54(1L{PnxMX78NMalthvaf3<&UVJLk(L0&i>cv~t`kPQlT7>(*}H1DrDuML z&#pnsj}iSSgaDoU9kBu@j!dW!!s0uIgg1Gjkw^$^t~YYX$Vhf>LJ~xgFy)qYuKAIt zm`cOYoh93?i-Jw24dz5F3} zx4-{&#fZzVUpI)DSn8F7AEzNP5)<>vk;E=AnZY>bP*tUA>kF&JCPHttE>74%++}~X zU1NKH+U8D2f}|nqI2B17K3g}n5m3wa%|UA($QkjGZyv>257O$u{Y*RpyyVTbxdcJ? z5u3N5(Q^j3cJ6IAFeS~V>3bVbgt!Z^v;IMxL3Ir4M5Non>nBk6CPtkDN6-sL@V@ zm3xtKp3NnG>e|kYFUnH8MDm0|KFZ{Z*dd{WTFyO4XnH8k^|h9KNihy-j)J>8XJ%M2 z=x>AG7g!xRJmbG#h2SAT#L9IIOE-(MqY^8$GF&Ni@$(Hyq%y^*0cy(H{QP24%I(SQ z{2l%W+K=52#vCu;h*0O%uYBS1t;)o|8p1z|PySw>=3nv2-^xo|o=W}`eDe3^>0IBN zrvvpl{@VSyzBf<5j2C_L2e`gP_Aep6-!VW z`Fr#9zXE%gAAN70{#VfJ+m-J(e+vkGyYlxi{)(0WSN@7!eEa3^oBul3f4lPi=I@y5 zzfPc+AALIye*5L`F@wLIl`gM*$5j7ykob1x`;)&p+25{wfATj6^zzDgO!Z&x*|#g- zpZpzD4gLo-ttqM#rnqu^p-qTM9GB_t%k#m6Tmqop7wp(e%0r(~w2 zrlY^jc$fuubMI~hwRW&_*14AQY6H{9|dk04+XBV%R-afvs{QScsUPnenzln)W zNli=7$jr*lDK053D~DB7Ry8&?e`sm_*w)@ZFgP?k@@aHzc5Z%QacTMU%Ifyc?$^Ej zgTtfaOTOSh@PDxd{QQft-}u4;__~IG0FQul$rs!;N8lek76Rfe_UqX9Dy4-_ zE&Wob&;Q+`z#ZzX0=<@F|JL~ryY?3#ys>*aX0%PT!Tzk&-k%t=B~F-Lp!nG|$CEyn zpsJF6>D}YC(Rtuf*43264cQ=}HgBVTzL|w4_eHbw@j7X^^Sa;%M{=_Zkajq zJG&FYvS8YC^a9jav!#57LQZ-CYS5=Rm7(~31Zs(R zbrDauTEg7_s`K3!>vrR$qUy0Z8=T3 z4_#;KOM#Vr`Jky{YyxWdU{z$apg09`cocHha&9KUxrr}fY)|QjHDM(C`o2!yieYO_5x@yU=9T~^Kld7$~ ziCkBsETIPD6iR1vV#4jtN~FZEqJnOGY4rSHTRU?ZF(@o?#Im3zi^zp%IO7c?F zLNoo1WgET2-KN(DWYg-{X^FYV)2G2NM&8#URjCq-AQ?ZT4oelb3?bEzreZVlvaT85Y1tQQ#Jrf~bs4ur%F6PFbG*hRNC{G23RD=+*@&pfur$ptOl&S{GSqYpbxe`_C+yt?=xBXS=EYZ5tYN zqyY9GmPP7Dax#S54aEAwNt+|8a+=WA()vw`#cvjm+k^a&;hOAAL0-@*wB&N*Goxrc zCTL>AQHX4@^M<q*F;~nbq7~Gpw64Z3fT9x>~GxosrfMT>*hIo}?tSe(w)miSEoL4(wz*TcI?#yQzS@xo_iQCA zwjv2C7b>(p6mls$P(N+tkS~u*J3qMqT_Kv{fEz=w?@O`u%E^&LIb{8#yX*;gSV8&% zM8N^AsPD>gPO@1KxlFhKpjU9X*|!1X_puI$RrRv!(wXGFF@4Oo)@6CIl!_@ja5>-icQSM zPoI}$wwE54Wt^7SAtsXCA(qZmHXwte25o;N_mkmLtRNmn-R8{>!?}m5P zlB}E9!}INak9uhIt?ua-)|T&vs=dmVqDutKDCFpl1>M*2J!S2fm47e3iPO_-MaHl( z$QW3Y19!x3%~j7DiaXN#fy-}6g1VLYWyqUIRvSg%Cx|XuxzD(I5LctBY$vN})0Vgs zm!$o8@N|WpiQeoE(EG>0ZtzVX7eljVD$`OSs~aA^ESW1wsC(#2Lc$|WE^vwRa|G__ z@+2>koWz*KsY3&CUg}yQubVk&gPh0-6Q$f}XG??N3=IdK!QEYz8rLXDqoTkNCJo|c zfR?Xl8}4imEKY}<7%dr*m3+}e`p~L3%bU-)!?s7l#N;*J7CwvDu+RAL0`xv-k)W$R zv1Xz^s@@MP+(fD%`t$UUpXX9;pA|9{D5U}|7J}wdBM22`u5u4;N)oa{wZ|U`vQct#qp(vY(|DtTDrRa z`U*B3E|$exmd=++`_w^i&a73&%qdX!n4(^EG?e#Ch93cY3~pe1Q9VL#5e!b2I-AUF zzydly#&&?TQ$XFw+BfdB-REDmIzj`y$39};ecC0InY7VeO0 zb(rK;4Ow!|L(f6!G%=Fr;vNS2Xqlg$OSnY;ZcScnDL5w54swyWKUE!;$#r_zvZ^vu zABDrb-W8+T=e0SX==>!f#Y;TtNk9WR!8U1CccFDoT|Q!92zdTYns-4UqS{SSa|en# zWe4YOGo$O7Z64*PYNsQU2RNPiu6Fe^F`twEy}_5tUaJB+?%I19&B?i&xZ2}I4wa`v zAbQ530*rpQTwh}d%XjPFm(&ZsHX`$uWvYG~-%7mVKO3ZWa3U^#k}L39e5sGt-Hl}E((_q%ZdMpe;3T^rHlqY5WxnUh7nZ~I-o-CG(I zZ=R9Y%=7ZXwt3D4Pem?3Ivs3hL@G%*V5T)8mwf^0@i)}kD65Wex{r$ z&e|sUKfId>i|rW%C+Id#t=)YWr&L`oOn=^?9Q+wPQ_kJ!5M&h7FriZHYlH@O^!RSh zV&u`&k#qEVdEAH)aD6V%F}dHr9fI>zb+(y{u=l(q3yy042323sV)3d=yfTlKOn7xz zl(YWMC(Josv`jL|Le*ZbtgpEgi~<4UQK5b`;hU>cqi;OJd5Xp`50p^^;N%CSV;8m6 z)sF{GyRj^U@wii%TYc?FOD{kM1QeehtY=szs#NUMq>LU}R}0i>)hNsSK00vEVc4^6 z!H$_8wDAx42`09y400_SXxADg*q)Dj7D zERR-}LsHz<+M#^4i8}O9H@qgVlO%y@*1IkVZ|%_Ru9IQH#@m~p1DGQ|ruksCghfY? zs4U(iL|7&r$o~rQ;lYn0V0W@@@)1}oEV_>o>_6o}_-6ExYnV^V$7k%prsxhIo^A7Z z+J#`g$_Jw|G5N7r8j#HRBS_Bc8+%@t8IK^eqb8bgR$3^`lb*~7b94mPWlXULeSzes zjnSG|5P1d!et11rZPS^Vu!BR?F85(ikY+XW8z(gYBHS3XX z)2r&lJtl>%O$)fdVKGWPGm#G5Gq)G)sqi-OH?fQX*92HBqS>KtTK4i;hvrH+jDj&?>Shv7NGLWNTv+-{!Il<`m?+CtbT$r}Uw}u5q|OplU}zty|JpK{kbg$6 zdtNyU@pU1~(JvV(6XoYU2Z9BkxlI zMu}Vg3hq?A3x2H%PLuG|HW-BPFECwJ?Xe6;lya$6Su_36{VEiVl*V^)iO`$`H>iW> zDk*%$=LSBIvkicy@V_V$+??uhMRB%%Sln8MrMKMismQy*kVYp!f98?8$tsVY`fu~{ zpV(otu%)W5%qBA8_3>3v#?>)jKet`FUmZ3$JQD@h*{OC{+r z7~JD6u$+mbb);tAX9BrlL*KvBuWhvg|WB%^4 z6*7;2D>9Q+Rdvp+2it9k zH*o_Q{uGbiXZURTEl_ME-PBAUUD+&!Zxw5_aBB9UdQ+%sBV4%b`kOdVC=$xcJ?b)r zZE^3dp2jgZ$FT!nK^_w`nisUTX)Q;($e*(w-$ABN21O_dbKHUY@TX*U4&0y)WTPET zznknXh37rzJl#xj)1dVH{h)mplW2*nNor~M1Z8E#UBWxDq6m5P@IY8n%|(%^zIE$7 zfA{{t@p+wxPtPkso~`AkV>`#OOz%oK_NcnyovG-a)=iB()F6 zn$EGvxQCY(B0V8B9Qy%GaN#-qjXCO|c=&ku6^(@9+-yxZ_FiU)4#ife@f6j&f#nxV zPtvL$)u}qj%vj!k896yP5AIt#aA_%@W1v z$hwCH4dhJKzqOK7SF(4-%QZ86Wd7u)I_K z^b@?GQLtI+Kn>Q6=KXf2EJ2bK8hNyw3FB<|VC!~49p5=b`T*#H>z2rwo8|>*ML^j= zbyoo0bo4)q#(aw_KW6i-LuKQ3yEQvS5^@;z;H>Yw%Qm`v8fAUCA{c`9xkbPk?dhb% z>;P>kGw-wAJUZGjk9`%3W!{Fuhk>)p^d->sl$zW}CEIk-vqx-#=vkl7skUmH2Q*m| za_(+D3S&2P(tB+sWiyYR*%6QP0nED2GD^%}9<$XUUH_sTiekb!DT_3w7u?a5vHqqc z=mJ#f{f={D&|3ABDdN!(B*fS5hHihf`?~FCU84NKp!cP0H$$)SAwdM^dGos1RH_G0 zZD-G98)Iese7-cUF{({zl%+HgMM{Fw62G*eUEciu{t z{yLx8`UCm?L_fz{2j-1aN)Tu{LQOS)+M_m3`z@AR_f(R)QV9G9;0gaY2x2tMfV%KDFxMpjBR=ceLS!HMb1 z;Ka+nf)nbO!3l}W-~`}+s!FzCKCCPmPqsVoli;bd7jE94@mf>#Kz%MuX3{8oc#18% zKM|_FA#8h8T{P`rmb{fP9ORhFbO>ob?~*d`6MWN7x96Zdc7uqd$%a?R#yG-sb+#@8 z`djd-?;pPFpxo2WlgR&095Vg_1jHS0>H%R#3c&C0P1xo?_&@baAelPR%%(6o0A zAK7s77qVigezRMP0&CN)J9lzqc?;U-&r~iz?$EEdG$exS7m`l}E|g_Dl7e^i7Gxm9D5!rIm8&q8Yc9b3&hZxMS)mEDY2UF{5*7VGUTOs{2`} zj2;?nI!NJI*z1roi(}e0Y{V|r+%}{0BrlssXT{gUfI;!|5u~Fk%Wz;lo(sx6V11?Kr>sAo85w>Qo0&csSjqS-hJGnCQd z23fy^R(aKsTd{j+-+Yk05=$PP)-xA(%*0>moq zHtv%oaR$}CRmce#q~G55{{A**Za`(kqI6RBeno9mXA+j$iieGM*#c0?k60jOLK3gT zz~6_mIC>@?URhAi)qSQAYi;_|Skzm4M3|3Zy-)kjrutjl#wUWEA3K|^C*q&YgZhjB zUDK$U4mE*g&K*HHtN+~)D!PdF@&(klI>XcsgE7mx7EH0%)XGYGCv(L+)p$R2Y=m<7 zpkl+ptXe>=`TddegH!`oc`g44h{}n{UTs?c5laPLuKo{SNnB-KzP*R||Le`k|CM2) z*{!hvC&iX1HO+*w$auAWXDfBlqL!FWQ|xevEliX2N!NS86a(CJ-s&|XK$cxj;D|aJnN4 zjA!8kF#L$~BRu^6#^dF%F_V2OlVSW}CF>)H;q5#*I;pXoJJ)6DJ*V=Nk-p!H`(r5F z1)HCuSZED!YEmjh=-7@Hl7MMy3pjVCKij!&<2xc4yz?UyKttkhnorT#k3I{QwhWw7 z56ZPuXzcJOI5t&jRrE?_sTWnidLnEQdJwkZ4_-(UDL=8|fCC+LDGr>Hy;YHLqby=$ z`D#d#8|RmnIHyldqGqIbO9Rfc8orzf^KY^YJGvL3M5)VVU`h8{bD4gu|}6MXKm51ZKtB#7JRA5g=WFWc%kb_IH=zu(jLAJh8qV=>or)LEEK32@X!t6ln)k@=@(Qg{i~6u_ zAdss)^GbT{qAc}YPV-lQBBS6g9qkycR@ylz-00Nd<9Uhx*#I78huw+Vul_ z(u5rdB={>>(g$K7S{f%3^xLWX6)-2qY#QFf>9MzE8LU%TpmbJ!L+1=QH_Ldb-yi>r z8mOJAj!k@U)utwRwDs6CL;Hcfkr7oXM!$OFfqWZcHMfnf_nTItMISkFI7%xSY$v9> z5l>y0ilOYw1Z+1dUaPm=;oBt!O+6L9E8rgyieUqnUwEZDc7K{?qGY1IOF(pd-bi=U z5GfZY;yE&WV{0R5Ea^7a0l637=uJLCf$h+rat2uu$aIr!vEKX6ObM*V7@(?%}hn$+t#{`JQgR zW4yQN^!Xm;f?~+e$Nyraa?fV=>%@(>3Ie&9$Ou(|1gr9P$wioN7c?x@>a(K}JsR9X z{6S9i6ynz2-q6AUw|wzbk!!AfbqvcUVL^ZY-2!UE_Y~~%PDp&$U>-mC{@di;K2y8^ z-2r5Sb-GcFlwF2!pkH#SIN)(4y+fovEX1Mt*}^i(ctl$d%>Y+D@=7dUtOfUN^xgN* zqytX;3RQ86;hCb4xM~g`6w!sKPsQY5oFMcAIZAfVB0kD*(1tNqJpt6K&_og zn{2zLAwOx#{2`a(t%ftm_1_ zBg|jDO3oiN!cs$BWvNLXZE1O>ugYnwjB?pDYBUg4M9#w44g#N)5YExl7~on-ewNYN z77oI%f2+AtBH8EMvGeFTzW?0^Gy#dL@3Fq-LjTFSAd~AykCBfbDz-k7{ z_M~A^$~I$4)_h{7*pHfF5fyDi(1_G9#(qW{Lu1^;icFwQhJ}ndsC4J<{>gSlu6TPx za9>onv*3|9J2#>btqN(Uc#p&j- zVsex%feoy)X`4bOXBWvQgK5BCGk8cuWaGeY!? zqgNo;Kh#HNR+4=B;_xKTSP$Y;lR^6Gs*=#!2BivbR)b*FaDE~kC9HNU zP)GJh5IHDc-JP+0TB&+)*4JWVP_Bt9E~GPT^AmeK8pP78UMNTVD#_ATa)8|drl9L- z-`5TqipTyO{J&;ZmzIB0sh07tM|S%Wj$iZd=Hk3(y2)sf^;MUZ)yU zo!rN~&0bb9CL~7jZH@?@4zPelVbY)7hF}S-dL;fEVHk=P)Vx9=c*;s!o6)%%ZNhKO zmZfRq%h9Iv3Z6xq*v$l2D<`IsVk!T_GBK#AyBDrOJ_;4Uid4S}@om~w`@lNKbc@Hh z_s@C_J~{P>u#qn1Y8)zyRhA8IEEg#U$W;(H@`ddfqU6vhb;~^$ZIHh~O^p=W|HG~O zz66<6}2g9DlM{zpQY1Rm9W)h1c}3)l_9qFTmz5ZP%=H*)PLXhe$q1{P=Dk> zsl^yCVXN9zP9~srHC|)mZ##i_55`w?Zg%oS`&?tM(nJmZ#T~jOfj1jCdf`%nwy`UF zBn)6O;idK+c9ybc3*4*;A02}Cj8wx3o?b5gbt|-q-Xc2Imxkcq4T#kXKpCF znB0cAL#$~yH;5FW?A-hUbnxE1^weSUGBdImvT02jU73(F|Ananqliu(1gxdfI9SPP z>yqwcE89@^t5i;AHg4q2!xVLD#-65LV;2v=LB6rh2xfX)F`+dfE%0&HDnm-6{q?B((t<%DbB4^V6GKZK~j~ z*;;yuSY*33S=yQ6kjH^X!Q;;Y!~NR$T4aUXWh5xP?To*=%G_q&)-kh!S$$Zl?_g|O z$~EE>5#gmLtFl(p{-Mp^=NM00$+4b0Wj_G62V2lx6-Mc`N5Ah{oy&(hfoJwgeo1Af zlKO2Fo(^dy2(2pqIou4&5Uf8hf6uJ;F`>iMq!=luRK;l<;D!>tZ`uDWusfFi4=GFE zGcvOU*k@XcW$~$mkn#sI&(ULlppmcPXg;L2k$lNdip`OBX^naA^_Fg2pHi{wuqBLo zz}zoI-ND=q^_L=zwjF1d1z|i_ymESlbM}`offGzt+_|vc1<1NScJQa^4oVK#xFML< zGCXg*hgp=Ro58Qpu73*pyj;lUfHBF(iDw{j(CEXbdoWK*gPcfUW<9VN|Y zPE^yX(ZJfj(m>`=C?w8HRdDwE8R^Np@)Pah=}=cqh;GSe7u$&yM|4O`=?8;Si;a`y zj(6LM9$I`|N|jp$0k&q@<2|~C_^DQ`o3_*oop=h8`SGbBzZYpY#l;3rtP_}Kr(kMn)g&Y4&RwOLi8jv&;8a0R*jDfolnmEvt$6-j zgMRD-PKF1eu411;NP0EHNa&4*xs>0uJSYFK~1O zYf=TfBKkP>OQyYIN`0>A38VlH{@7(01)Qk3w{fts74##?U%P+vC4({#N$$R@D2pO7 z%f=t_8oy`lF1V%sZuXJY$0aUCUilpE;Ok7<5Br(zlFH6rU@}YsZo;~1Oj=}76s?~S z2xJEW-9eQ3ZmE88<7HR7&fWssJKb)>G2T+D(B6mOwd`eBRoKgZ2Ipsde(gT+)GHrB zq@GHr_mWK#ZDEt`WVl54;`sIbL)!N z<7?=mi^S;AS9<+{mtbjeD&c<`kSNuzgvI6LP=Sl=Rxc0k8kF{#>9fo-)3rSHY{nHZ0@KF z?~|)@;OafMvdNR+%ald9cb)3qF56bf7nF!!Ml%GhAs_kZD+MUcE7n^BGdn32iPAC3 zw%jW;7TwPD*+TpZ!lYa0(X+LDd0YabB_$?WklRH5KDJRUQtx|o-f#FH0kcQY3Ra+det4~kdLvO-*q3_RgW8pd6 zM5fiuQq{)|i!mwKJ*=kZ99}0WX(zO>(~~A3#)7gCMO4X0XPTp`vILez`FtE&vTKnJ zbLC=JZ_~47iEAZlc_PT1U*fs5Op3var=|^m=)RtpAJwgb7$?vmR6Ehzod-N7CagJ7 zozJ{z!kG{J$vu4h+#NXO)ge;omjfB~PS7u&(yRUFIwxYEX22rwD(c3>2}!=CT|x_k zG{MqY_Q;0Dn-6F3S&{W$dnR#R8-K1>vHQe{r=j?hneuT#zJ(bs48O7SE^J_xd3)KG zh&~?4eCwJX3f#gbr3=p}Oa1Z`#mS7^zBJ_M!1w$R_q>p`@OXR&Co4&I%{HR1hAhSS zAWY-aSSOA5!|}Y4r!r*kghUzEm2L&!@pPKc#OocZFII>4)QB9sPhF3i-u;4kwsl*O zD{AdtGRlXU_5I&Gx+gCAwinKYHtd-LgH zD5+{8AT-P)q|xD0V}6_3w)msS;HUGE-e+H$x-2%zOw5@#){SDF^5@A78Wh{#hAx|0 zIf6JQdEi1F!Cml@L+g30HKHA?aXP5dwD}!uqZEr2HIXVhLkv z{Gn2)2!M%!l!s4ukJcM5QVZB{27djRCqn83Kp>j4)76`DR6k^@ChUHiQYmZZv1Bjl zL}k{`2}?)|easn;@_9v9zzsmD)i;7C(@kMYciJpw*K(KRBw+Tpi^oj^Qz$A)cpVjI zEOeg3;Y|)Z_+jLuaj=%T>NwJU3Cmlb8dbFJ^@)0vaR5-9zwK|LQ*k&%~rl zw?m`*pnT}a2aa;@T6H~vaceU4O7^Y3WtvK6;E_Fb zrT`|@{?GS}bcdIuZZ7h00y)hLf-@i~_;`K7z&+*j`oijk2ayByNsD@K;$&dMg6wno zF57ziodg6)`nBfBEC^_g>?C_)Bpi2V!biJhT<8Mq4#tGMC!QlE-dKFbf2|$)+`e1; zP<+W?a6KQaW07ha&INO58MjNWKkmb=et!3CC+{BfJtD$2-5@GI>Y)26pq$iX_~dNJ zBrJ2WaBtyub#Xt-!Rioo_oE|P{2iSJO0O1lwx&j=xbY3O8B^lO zh5J*gq8-D#nSq{+#LJCRSRzS=+WT@*C%qzq**N`Zb96ZSn)sJpdwIx-fN zk(&kpHjT{iv-)Uy2oi9^*b!61h1yjN?jdl5N+2P4P5K@_JqHv==y8=cyfpNX>1%K6 z&?!@}fkvPBKG$F?3kO0(6&d7+)V)MoERcBl>=&l`p>K*ua* z42RrH=X2)(bag0vS0S-QQyA&RrzVIQ?sLoASPr_)&!t#xpI`4AhN2tS(e-WEjT4o1 z5zot(1l?kceN@!`IR|O_L>gqpzd=0)3sK*L&32s|S6ardK+j;OA+%c|uMBH@?Fv!8 z)b?`)bUohh|DvKeTBlSIIXp~oj&J20k$jT6BiTO;$9AImK;aeiwLobFG_HZpmwPz_v8^7R?IFyO)HRwwF}Vo zje()^8en~gOwO1LO^+?2y#!@uoom2m7;$FV+9aqQObS@)oy7D85gppECDV~QuS?Qk zi?e@@>=<>j+;_JeP}*kA#CU73OR{b?HWp{*B#p)uN3>uVK}5vz9J%p@l#rOR?AkE^ zyTqA3KCh{}Cx$BzO7Um-@$Y{z_ttSyciY?Wpa>`kigZhel7e(9(g+A5DWcLb4BapS z(hUMiD&0soNO$+pUD6H1_^&&EJp?PG2l_Svo3zc=9A;s>4cx zY>pO{Mlv^0V%Fse9&l!(#!``a0^nuuna4cZsPOQBU!Re%cr#$dN%U-Dlj*~?Lccml zxF4tn4_S)|slhZVVvgCO3Uk7S2Zn`mAP;~oWibRbD_x&N)YWpfIMkYTQ@W9aY&+Ca zpmbnCY$qjl#o3#8D_*G^62(Lvy_3Z)3cZ%V809(5)JQ# z2|`#3?}w&U7mDe@^ZdcyjVd@+@0+kxW{n2mzNdP})#=xB9xKiVcjfn-y!& zYN$%gOXiG%PYP3DD6G~bn%yhpbuik!QB^y~_@-l;9nO;BGHBTFNS~%aMFssk&n3KA zYXBlUTd=Ot>|_MY2MX>TXk#06jdNQy>^u(m7`s~p2PF6F3kmlO5X>k72qsxKv{*1y z>hU!kxr>+Z#O5~nu^)UO@#1Kr3prm-1O#ZC^B3;#N&-oV;5rGg;glP z(O3ZvBcTBH9GCIDnA0NvM?FBoZ9K|B0J6O(l$eqw*dMSz2v`VzDM>o{)O7y|IJVV1#nUBl<$mQ!@%`+32QMdOZ>$Jn{#dib!df!Zkl82B7D;3L z4iR`vAc?Y3dldsy6oq~Oh;yn4{_#lGomvFeri|OyBp=ObKSs74eU2tJ>to6EG8WkU zWYSde!U8u`Uk08S0(vnSBu-t+x){R8JU5>QdT@n;hItSjzNK zDFPjdWj9{?eODBLKFx6fWWD)P3pcNCIl%NtfxQHfN> z7L`v@QVbTghSgzx0$rbs5fKEIjYeRpQ|#cpc`O#3Gu~#T2CtHGQzu$KeYrHPyqz6( zU3D0}aOf-TiTs!EhY!uexZg>OO=sU67W5V%Su7nwj1~B0zw}S>G102ymOpkU@aN{{ z(+alDa%aA=V)vx9aHGhR(a54?);4T>Tb5JWXstfB*_7_B1dU5+C8p`tlnCd6EmKS- zDdeU>4s29!YqU+f4=g=gYFoWcy4OtJCdD#26Z0NJ9%v&RDYszlv*u){CmD_g2JA`F zupz6gwymZvQ9VZFTYY&A#>Yr~Uy0qX-*k<_5ZkOIFczG`@LrKX3^^Cn-F%ySZ@V#w zv(-!G`4PMhlRlxBkKF-5Q8WOTO079VBhCoXQ-?Ui+dUQUXuO*r>*eNw14V2$jtY86I9GqThQvTd3`Libg9K@kM|!LIqkwOaQop2D zRC~L%o~TP!K6qVB6NKTLf;30LdD-H?^0{J z5W7&R(H}a=`~>+`wbX_k*&+-k+<;O2VIFi)iavpB1FZ`WjI=21V%IHm1AfIg8F4LC zVbT~W=$mO%J*V(NmQLi8l8Qk-T5G&)Q`|Cr9*3!HhG&hp*C6a45+z=mP(9NMs+rtX zjK$19HuPh=K5j^?&a*8WK^XW*-7&FyHfZRLR_MFOuch`H=)hDpM{{3$GnpM^28l$0 z%oqN|FRrz%{vL^sNtiWSsZqCKlsQ&_aG}FtCe~ zsR1Y$eL=tjxiRC!xB?7xIaP|fjZVkC70n2K!d=tF`g6kJrE@peyv5N&Q|jv#&2& z#s>^QOplWk0+$iT{hduP2OeM~R+t~Jiy&6)74ZQtz5gfZv2y%r+*CXXH+ezj#4g}>RWH&q%)tjf$Xf2mq%83Q=|O7L*_{jCv%T@}#U z*Sgc>w+wk`s(cvY`(f+HJOi+4`op`PQdh-i*&qeNy6*`bzPuk@a8+D|KgCg<_k+aS z^a$Q+wKbn@)!KpyPz|;9T)pu?zRbLtVy8iziXCO|k*U7M5p3bhAsDc6HU1bH$l>x& z(CI{&y`~(nEYrYSP*lYaisZ0cNu2$iLgfn__)qU517PSAL&*o+3GEM7Xw264 z_=_@P_xs6*_QrFe6E71*td6<%cx0omi_{FM0N#&2d~Iguv; z;1${vfNJg^kLt^b>(uEi40lH+>&14N``kQL9cZ7QAv6Z+{ zbN2SqV=kr^5Nj2zV_>IzkZKSt^)?}_?r-M3LF5;ClJ9L^Ug^F9;5ze>$xod zm--N|JhF<(wuM?+na2Rq@j{yH4s{Vg4ge1O1e)~aWOi?%E{k)cT0Gmlq z1KK$P;61LNG5~Q-tLq+Lt=V3tWMKN7GHam=<}HIpvof+e*5ok!iS50gp#7RtI*EU(1jXTr z=``b0>E0dsupIXVS!o&j9HR2nJAt=vJ)|Rm4>MqbmO_GZ5=IR1ID6}D9Z!;z32N)v zn!HT1$1v*^v>Pd=#23x`I3HvVl>oPC1oN4z7J!qQ^$(}~M~JRU;7k)xPnCC087}Z^ zU>zmU8B&8zl_Kw!ae6h|)lA&F0HQL<>p|}uw(|n7%Y8@Qk}x8OvRA8vfI(JO#!9U=ny_ zOGl?1#R@LPMZ1uGGcNyeC6lr`qY1fvM_fSeJNnX2I{TS_qX7CZYBSt8zoPczCDiuC za(F!KVwmxdnyY|QnPCyB|9n6He;H=Q7!EW+$GeB&xj#WR_6nEw8m3FW+`p;uFH1CN zRe*|- zLxfLf3xAsI1HdLL|HCG`?RW@i$kREoO?C#Z9(y^EqU4UV_1~?EP8z@gRcn4r*t?<7 z=pY$Rj*TnEGxNyq4ZXcfFl$eXOdWDgwGfr=S-;9IOSCC}8xqG};-^IP4`y+-n;M>4 zF~8wqzGx%aMQ10=xtS_v90_b(ChC+_X%f55-|FjwuV#NFbm41I9{vnm*>eLSBXzdD z8-zGKJIZ#k@X35}Ot~$`Nr{kVqzq*7XKc;u^xJc>ZMaU@AoED{d#BLWpW`GqCkWp^ zH6pVfKnfrjHjN@Z1&peWy3Yp6hvFjE?g!i3dv+BQdfg@@d|6B%C?=1I9E-ia%d1k( zsGG3-Vkud^Ke)$s(w`Q}P)~J>ug;PrWdX-(yLa=O^_CKx$x7g&8+>O&h`Uhw@o3F? zdKF#Vqb@umcjbXOapEjqjhcE8o=R6e_RX(_sh9+$t-cm)OyzRZj7n2b4<)fOH3;5v zE;?=TtppdsX-s3{w};|wOcf8&Z%7Yo93jeFR7Z{wRjpmSXxABTv-uKbPiz#OI-Pt>qtuGKjJ7d$HrAUMpPTAuB?sc3J#xoXg;9tuwjDpHg* zQ_n7Z`A#}_Q+0xNf0;MvT$b*i&4)t1{FUEJL6|qlrv-Yrero88eFV&`@l!J^G44I( z*|X{p#G)f%@B7V#LMh4Hfhdm2wiY3ZB;F+hkz4xnj& zu#+PqGZwFe*MT|Kl{fG`*;aX&dA61t^xg)TsCP6yCZtB{Oig81i#C*&=8@)=Q5WsM zl{l6!lWH1uGn3F-xe1lm^>%d=-XBtXeYP zUOoy<>Fvn23aNiXt@JQWG_|A!Ua7g-3{$o^^t0T>TdG=!g6n0A=6KphrwH3h_ZmFpCHSMbacS9Hx*pZL*UkHNQNT{bJ|}ncefnW z)}{iHf_sUHHiplNyb^A7!$v5DGDiAhMNqF+C=OEe!R79S7+KJ_voJ!wmfM##zu`p& z2d7X5HR~LgPriwb7)M8AjrL>@T9YfUKQXWE$arG#_8?xiw6d?9n=wOeCclsG zuw#;gHiU8^q>}*!m2dEg-29)9__m(Z{V@UxJKs3%p&A3T9~ErFkG=jezUMWd1z&bk zh|fO9&77KHhS40};e}Q@I)!Fp`4Pz<1a-^F8UJpMtfRI5Zj`aRfBy(@oG4NXIb0w= z?VevRw37?4@bVBiAZbJ{`TkGP-V6DD{byxUEwtk^3xZ<;{r4`W7Zm30b0HwamuB~h zDjnlRSr=^uj<{2bE`&nvyyo{badGD&A^Kmc;{0g-l30tGXdm;H2w&CjAUSfpf2AGw zUlrefbzqSgg{wBVX^(y2u#4fwCHm~t^%F#?Yw#09xGT_yr1O8{$YOD9-t%W)$B<37 zjZp{|^~3qp(Tw9R3MUlN))+eoc+sQNGx*e=erZOJy3*b=W`NIyNEpf2Z#k{0WRkSP zFy(#otk{QVVDdUgfsgRBWuX+_(>5({s2K#lGE-Z0q>XUZ8|)fUzW13wB}iV$3+?`* zvKvDc$J5(^V>Z=2J#&MQ-8@TcW7z8;l6&lVpW|(lJ_b58+*hW1j|O6)rH*=|IIoSE zhMww}HsEgKAG+&Bh+Wn4G`?~{Ac5E3NyCtSrvzU%>DCK%9ivWyploy1!a|W>%9!mo9VLhvxdukh8D;Eq2^?y)3H9U z`377|O2%?$pcsrBP8n&Om3-=sZFKn5<@)h=l--3udgJ)>_7I+}|2WytVj|&(_x>BXpf6wfKe2-TW|1=f$uN~_ zP*4*7WAp7Tzj#9PrJdCOk^cWT7SLJrwW$^y@Yh(#=MMUU5`YIwT}h1MGJd{$p4N$J`aFh($apuReG{Pj|!@M zj{*!pdB5LLkQo224}WVK{OvH|lP@(#x*Zhm z&&$tZ8c_7Cbe8bc9UeGi`|6f&EJAf5KiVN z-K+A(v42=(P^<&_Z)6a|Nk_3-n3y}nqKfwUAe(M+*gWMtP5H3|BM%iB&idUDYx4WZ zzG1{&>Ibo^gGOk*v|Vgc?>cS5vmp%oA5zhY&|*Xsklh)SIYCG+(w1tMT6$5@i>odh z%ZB6Edt{u!Q)QVt9eq`Mj{VVnXq7J>;rPF0@NQ`hOh_?#jBLK;t?K@Hx>a4Qx-=_M zpVpH(<6e32uEo5|-CLn(!m{k~80mo}f-Jx#scgwH!BevjK@M1!fTawWmIu(9_qqgO zJjq_=8u13DF`)thNsHq~->R({Wcl_nBd@-rwLnYh>@ULO2N5zIgij@JvW??8?<}&T zqk~nLnBPTF(FRinNzlPL+giUPA1i5=LQkz3xZsrN(#33$mKVT{N#Xrf=!7tGcMDRa zc}G;?Ubco$NOyDd{jn!4Zsudm&}L_vjuUJYrfY6;DwdbJy{O%r*WVdb!CwfQZRow? z8dMPPaL4!1Bh&6QF8;fTW+dlKJFZ^NuuXO=Cr?{KP;T<7t5NiRb+>oK{ZQ?bSm(zU zZ|wA#puv@Eil5FuV6xq+S878#c^I1w%zBga^$R+3#=G>C{?{jhseR@;u|r@Lx)%fG z*9-XBr}i0uD!f6pmJlxGgb!a^!`{z!DmoFQrza_L-mzbOq_!DazeA?LCK>KQ-X1JmB~Lu<8D<`R=cd z&r0HXzp`|z?E^d=EXzR?BXS#<(0Nkb$AlWU`5twZ|Pr zyhqBYF?zyxV#k@osc73CK5R6W+~d;%_->S9;kdyka2DvvSFj<7D08oqaw@TzQ+^%w zelb!m(gIa5exltqh0qumbtmX2IT2i{zaZ_lSJl+};x^e-nL?6=$0P^bR})Ig^>9GP zCLDE#e8);(4#E-qV1P8W2+`C(dsOjpzY6E86j)|kM!S3HGh^629Ws;2L(duV$2UzC z2hwDZx}*B70mgXYhycH={4#YgJ0m66EoHToqR3fudLeUM4NthZW(cWb4_v>DEID1qx7aJ zkSHY5H5kOr#e$1BxO-xUAVt$xDOfm!e(qhae9)JF?D$j;eBIsW&5wIwhOW4^apW=L z(+}L`nh7l#kiIX)Wy5zm}p-FH8zk$pRL z9#`HhM|Yi5u>s#VV&~v95w)K8r>1L?J5+`IX{z}zaQZ*}0kg~0zyfd~RclbymW#JA zj|RU74&oDW-hfSZ8S_IKd7>q_4mj#bi7CL=d=~uCJ4X%y@QGjEpSvvoK{U~J>#ER% zM7MTTg<06Z#-YDZ7}wQrBq6KH<_7`FxjEn$FS_t<(%`h-mFi?&COBHRDZDmmR;~4p zyJ0Pq)<3`1Ppw*sV?bj5)qQUZAA}vjn1=355xL)sezf`$N=q}Q>(ZrHJX1!$w*)3&n9a?i(sq&XD^Z3nb=y~x?FwuOt{h90; zX?hcYZ;Npyyn7^*Y0tQ`$I}qGNAEdvhv8eBZcz{xiTF$LH+JdwcL@m_X_JNnt{K4C z2lD6=-*?~bnJcHd{e(d}sHDnS&9s_N`*DYOR;<(}e`Q`~q&hC%v5Y_Vw!!n}Qrr+K zBgw~iN038Yw%9m9kJ{G{^EUCyQiqnhS_TG8MJUz3(S;MY z`Zk~)1{zH@qDGmpn&(g88AkYfapZu%EG@`-&0mvAs?kx74)xTaGM1@%%!K+ZNO5Q4 z@WIjki7NvD7}nZda3Uwej{Uwr04uCwAo`H|I$l*Iqa`4O%~@!K;S;s*TPxDxMf0ap z$&i(tIB&>&71i)g>reqB&%n`$b&xrBJJ~MG>Kn3_6&JfsE*}}p^*~7+tS`ANI`GKk zhTGje-&stgy-d5$2?;$cYS#=7>=6RmKSAb411~S$_jet1XEJ*c@5qT*N58!UBRYc= zAuCpGuHqlN9x4=^9nZh~H?Tw?gZwLOqJ2tt>yY@y;VT8y_IEmOpKKfs{7cX45r@f?Z< z9Krmv0=hyUu&a5p`xDgVCMEX@noho+jrc&P`cLS@NMJ(@_zc{A1xSRPQuKdE88nsW>%_gx+{HX(+ZNLC3*0DP3BsB0DMj>r1IsZ>OtnuMlOGLB1{$ z35UU6ZY#4DMU)4Y``#uu5h*l5A%Ws+zBuryCmQ!b_4wk5FCDJjzb^ghy@3pc&+TRk z2xrr%O+a!Ox4>|=V3+QOg%jOo*QOwFFqaDvEYf;_1*bOGKMra3xH*^5gvCvq6T=ci zuh9_E>5(a08@a-0)&a(xP}AnzTGr6>g~rQZE{%=Q(vm;2mm}3g9TWWt;?>wD$u}#K zLg42Ns@cTL=MMg0%f{8`D77~b32vBf+0FVI{8GOCPM9~VUq0SzG%n*OAmZ2Do4B1G z3>JqBMt3VqD=Xa_xaNx=Qs`et`dq{Py-}OJZRIE{dIo}hAk$kjxL2BBXO+NCKa&1H zQ7>~weD--J<=j(VE+q5dt7I-2aBX)fKi=AhwdU*z0olW8ooGUv(e2y!EwwxQZm)`M z9VKp+0>IPC$79*X?QmeGsS(;`v4C2WNlzL+@OYBrY{_YFFY$x@Ua=gPsHMc!TEgem z$|%oAeQ!OId+qR;1w49 z<45*t+9heW{+d3xJOb@M=>X2+2lxnQxZrY`2mTGj*THc;wEj2|O30(` z@*_0?rO6~_W@vmCS-IsaQ)Bsx8n@t3L*ufy&N~NPcJ~XO3x?V;VkMR_H;JEKr?_l9 z{L}kiY~QpjrRu6JmVLjc;cs1aVSyAh(d55WBxxRg-RLSK#iJFuH74 z(PmZ1(Ojt#8y8R;Eb$#=CINcsXkEu+>|tmq@RfRsDe>0#Z)#F}!QArS=|nRc-Dr~y zP<8YUmzHQxjv*WIow5>w>myLNm)!+wj<1K4^ZobOl!ipcq6RXfuGX}q)tUEUIK6ao zzHR&0Cjh_+0AhN{Q3w~j12wHG9q)IW&yEM&=bH%JuFE!R-SRk1uSKR1FLAuhJ_J9C zvM@Jhs!*3WAk5D>hC7SssH2hbr7cmV!I9v}#>A9ZL}dVQ{fhHIk?5dSn8)#CM!lKD zirA^2q_C%vjW?&r;@Q^?^TyUrgW%SB9`&&w9PcO8I=Sg@m47QP#C}K!%cQ%{_+AF7 z1Bu^HyXBa0IFjs_lKB_BcMN&=^NY*0ZN@aiZ5!CwN#FC5Az%9mGC4Y-=eT_8uS!nDmbgh} zcs9Ufc6Jg!K~5uZI#s}yq}=Z9kv-l6dK(iH44nXgU^h-fPrvyw5KZFerv|(>Fc1~} za}I#e(~X|C#Hnc+;$y#)-c|XtFXAqkdcxlSuj@c7|Et`c2EzWgEk7?+!TQdFqK`;747f1sp(BZ4AlOCsQGlmK)c68`z9o-Lg}@Konh zzz&?yFJL?NBT)VVIr@Kp?!VKT%cYWhz~dA`YYd_tBSkF`{GlYcT_bxuUuu8G=b^fA zl>f}pS4Q^itALzWfE#~Za6Mw>e*AJbmgb$1W86;o20)(`fA*WwmitHfPlxJe5%uQ4 z5da`em3n7C%F6W?$h@`3!Yz1SP*-<+Fl?%=yQwTZ;A7Mlz1jIG;Y%VGpHe!ukLq1# z0(Y)IbXH<0d)Xu?yb=1+ZHFvsYHH2qyw3d}ZzIcS zMsSLHiz`Dhf{HOb2)N#0qVeA}(o;X1?u%uyK)Ja9BvGZow?;?KwUrx}(Bx4SubTuH z->0OALAvSAb(I@8KFh}ESvR!{{b3^KquF{e7(&ywhN^g5IAc!CssTk*mh$};+b_bL zGr4K(!Qo?>ep86+N2YRO?X%1~-zS}OX5Nr$1i4Y%336^qWs|+5Z`a>8Fd#olGb&DP zZ(pVY)*h0%>N)rFUCE3IK_}rCzINF&(K}zD8O08=@$J)Ia9+~oXdQy`{lcNz>svoT zcpM?_FW#G7z3OkM6Y%g~SxN=vEsWF15;gvw z>F8);Z^M-=#!T&EC+Lu$N)?HM%&Uavf}EVaAxEu$C`QLZasaCdY-46b5PvqX~Lo7mw^+ZdzVbdfn6YA#EY8@Lp+ zx6o4d{OE%^)-~-{za2IIp|0;A8wmiI(o+pROS9SGK|TZuc^)RUVs4H6d?{SMsrw4b zmJgveA=NrkpWQ%=N47p}sF=v1hY&o(&?So;D1DoX%EFAU)P)<_m#pt_ZiqjAbo8Q3 zfe|bKxe9FQxp(_BXiu{b8O!aW>jmq7eHB8)eOH2?8)maIQJlV05szu5{F~>}yT$U~ zb~N}Yc05Q@3aT|Gv6X>-uzyiiXarpV&=?cpK*Osm0K@(`7Pv|Lz93*~)mnNJFLrN? zXvr8{gu{UQ(Vf)$8?qIj{|7SnT+2CYt*7Zsjo_0!%~Ncj>(n?%OQ=2)BWF zu(mBu&r%0f=}44to$j?#E%PWc7FSn=H=F(*d^vRLl9ZwF;w-I#P{xFJCRb!ysElGz z2o?(U?QQq444E3g{sb8f0h&t*m_TL(4)oGLxOK}%tL`DxEb8U$eBVF1z{aJ#B>gjU z*>zy7NJg5}7+Z8$2a*=hA5s;{6oWMZ6Uo0NJXc0bi}goQVc1kCs*<*!a@uj!jh@07;&huV#zcuP zp-&sBjR@7nn<)Ihcnjq4zOLEp;elQ*Df-$;Mf>-Md4>JlCnJ~%_V!Bbs+DA?eiU>R zQDCvsA}sy z37?ykf4j9?#j>8nnR^K=Lt8m^AqEv^>pgAhl7oo+`fP8#ytHxSJ`0DK(lP^^&d4zU z*E<^nVZjwzz{GVKT+!m$EdB@;yoo4d1kD`lqmFGitKaKmz(E1 z`-OT1lh>CDWZKe1=MO#{PjIpx&NiCQ4~_3YPkZLw_GLrT1&NB^@E*BWC1FH0q(@!t zm^&Ogq9#(IW)eHW`pN1V=};7wDrg1%SNkR!IcM}KNtbwVEv1K;C`LvCkge~z0Wt52 zePfA~E)zKMq10C3)%VvyL7f=fgrudeVu2c|w7(b)iuZD6ZW2pW*%NzZtua$Q-l~49 z$v;z*{54R>|2f%6P85kQN`XbRWUEr|m#Ut%uVs1B)?#eW2$db7vYMht&YZuLsF3el zt&n#M2P4@2IkM9)W357j>TOZscsE3nv!|M424_8aROAl8lR(<_K%U@jp8EpwmasXmDz2ezF=i8Uv0QP|60(eM=_sanBB{bAkz?en~*v? zUH8d?aK$i7{1@mp*yt+8WD;l$IMfDhHu82>UvB;?~sg`sH>DXik7r&(11&rYAEV;5J*mL76olnd0^}Qf>QRx+k2)=_N#BTy>6_%o znsOW81M0|!mGIITHbrg-_qI5Ea|>z1K7qea!PE(-N>PzF0##54ydj0riSaE;^8w{U%cjXmUwEYWXWKm4*jqlVn! zm7I%CuqE|Z85;_-;ve>6|N0a-1Al-NG_7&4TLkdHPaIPzg)DCw$T`S=SmFq?mX9Va zVr=hy{Kd95P?58F@B7hUhRvnuMr$-j*K+NPt*YJ+g1Xqog3J5H4UhaQHX{HY0r{Vx zfp(Jq*O_)sdfNe{OGMrW_Vq;bX<&--lER__|M6j%@{DjeMP4qzW4~}93c4Bt@BkXD zSiP{ZT2U$Qh5VrEf1_Abm}_s%a47tyoqoZPwQv+;Cfi_*-H)|bxmWjF-#b>A3@GSo zF7~6Cv?nCaT?3Y}ZWif=O-zO3qwPpzx67-FOO;0mM$_rP-SqYQu8X|gu1h$azJsVr z(zQ>93br(m2doCqR7d13?i1N91$ia)QFybIF6fUjyc6N_^B=&F%xV5%o7E^&tX3fa zo4XyP@rtBO6uaf%chv8=L|FgWSR59(-$DEe4e#`O6M&IoFaT zV%0@vujn+4gzR>4E`0fnc%V;!O$ao zznt-2yX2)wUECD;<@)TUjDf1>*Q0;A?fSnwrY6yKM-jMh(>CsQ{MNtoN8QZ5!B#=* zO1{oV0=u!95{6jy{4x?gSuvGF=4qcoBd@lf3`~9r9&v!b(Iwo=YJ9b^_Bpc|xoD$> z=}nXOJZxG@zl$u5#w)ZYW+jRQ!~dF8+@hV8514_+vMP+moVtfMKlfSVK|)ZoF4y-~ zY!lET9oRBEb~@q&0_c$qI1%*gJm=H8@tYg39?pf0SJf?q-64*e>hwz;cw zP0=%XmJZj_FMev zm8{Qa;k+iayR#oTKdlCG>+qv~Nq}Bi%pG~9_Np-35^NIl>f4whXGnb3lWL`p{v#O4W<_Kr}n0qM-8m$XyOigz3F^(M^JMd8-KY+j`U#p@sgK){?xp8bY8%NF{`2@5XXce*NqkgZWpE($I z&r?IPS4Lj+TJ-yC&K(Mqn4cQBQg&8RrF!#mx*2|a4UklhAkWh@%Pq7<=>SgGsb{02 z2IL&dv(qFTo1tN`P$Ap`*>tGo`hHY<_07#>X}TvbzP!36cH;0<%IXVTySF_B!0hMH z;=4i17R2U^8yyZmbsj6P14jDPm98uLD>$4R#OUVwMteumeKLW;nC&YR?@F$^$UNky zR}=2aKf#gRLwhB~ixOd*WIO-S*||~zo%f9is|@OuRJM#VjZFx=x@@9j!mX-l)`325 z>4?1U#CSrgP?^e+4yJ-B#h2A98*cqhW#}m9UIj{OW0I;^dV6Yua%BWgk*zhErhHpG zVdJ%tDBXyX7mnD7qU*fPRkF=M807wKXGh>#V37vEbA)8rP)R&ddY@!4g1uN z;du2GkYef8QE8JaVt6p`HE`+ur1O+3>apr~pBfmOqN;kF3iI$z$%j2p^?;8vf*caf z$BkKr=m{`KD3IpsbM&f(t$(66jh{yyud>;)-y5b+NZ|pkPkG7H)@hdwd*Et&SO-Gv zko-fz;fc2)f}_LVW(eRc0X5kpONxs7Mf;|Yk@x4MTk#mEVP#^-lctThk*KcuTr^(6 z121r7=S9FYec52*tAX+;nnaUZ593L5B&DTQfc!mb2m%8Mwquf{NX;~LfgsBy!`m>M z+(BVyNG5Y;gm@Nis5gWnRh}9vOh|li^Lw4yq4T4*{48^}>vJHDxO{8p zM@A)#R|yd)dc#u8jtN1kVO>uRA-EO!5*WAdi_iE8rQIh0A$1`=hvo3nV=HERRL#t5 z>;M3@^f7h;JP-{HRxtYEoB@ZW$T+M&Xj2 z^zc5S>h^>bRE_;8Dzq|vT3WDygUH%1zx~F>xKGMXg_@L@m?s^IRxmdw&Ukw8%chq1 z=G7SxcdGiqDl~v(Tku&IRenruv;UT(sG_A})A$>_18QAfzVBGlwV8IJK~vJSk|p+$ zHfsx|S}fPsvvtdZZtW`AMt{bc#Kw7V@~G?ODwafUSAL}WBT0{@oPsALEbBV3%Fpj4 z;L`U(qy?%rplx>y-^B#!>H}*5MOBsW$AGGccSEm4UxI{7^IB z8lM2sgBYtk){#+zMp)@q9c9xKB1kLJd6oNiMpiY%y7)M)_Eas`ax76&8U0tRY~#HW zZNi?opmU(JlE5=KuSTpC1fc4{3gh;->#eW-@T3Z}M_>9eu1$*UjxT+%K%D0rrO9?! ze)miBmyyNRq$ilsM$=L@ncLT$tEu^nKd=f&@P2e{UI>ve%I>BCawHeREe2xN#?;^t zg#AyDR4e?gZR%p+LG9z7f(i-7RVB&d{9^m?i0SU+(xIjK5{B;|Mz4m{-rhGsX()Jm z5b>5C{Kj)?Tp!RTvL!Un$T(5;T~obJ(eX-BI}_DDf`dC z(^GTBVG_wfgepW5D!%?tUN+40fp|=n*4Y?&wiN^9ONu7!N%O-D@9hzI3ndPMW`ff@ z9yOwT4DGLg5)3;DhVGMSLn@pM;jt5%jeMNe=2KM|aDGQ7!=>ABc3Vf9{BX0D;Fhor zR$ty*AZ}PZl9>sL!ESxUSbzuoYG&=U`fIgSHTfLQw*4(OP8Yd~(08Li^5`c7a`|uO zD_F5G^sR#Qg&O)PLnI39d9VCf`_|BsrrM zEHHF4EYZ@;kD{~nT7DW;fqbLskeYK*pt-8#tA3JFb-~~e$VSmvUwii0R#v+Mtat6i zj#!Ww6)wlZ{at)lok#4Q39iVf3iXjZTu(8)#_C}Mx)f7hGTnVb2B$$n#N*hSGPM;c z!(;AM<}cpU0~Rc;7-@v0;?>AxXzXr@2UJgcA>gbj?>*bhC&z*#mc%{e09Fw6eDWD_Ml3rJnqUppd%pvoJp_nRwdL&*DldKXQ?8&^3xV~td~aWt5_ z7{TxFrL$M6MfQxDEu@&KLsfzxFGq(uSb|@+=!r^Wb?NnfV!2~XCfKeec8&g(ldqaA zUL0#9(aEU3WXaSzo=e(G`^nIL#8u<{!|V#R9i_u4{`$4_Y`8*~1r%3RMNc{rzu(BM zVr0}$4qoW%Ioe3pO<<-XK5B+WY-SiYC^(x15K&b}cp%&$>Uc$8^dn}5DwDzUt(sgV zg+W>XODd|RHpLbf6&IDuO<93ZZGmk4G%3uL73BnXLhFaHU|c4aD3TwqZVg*}| zjw_$^SFIeaY%>95IcS%ujp+Zv&5)J$tA|@+38bCXH)g8ryjCvxT0#q!=FLqV^8M<- z*FNL?3fAiNf(Jh`euP}hVj`vTGc#dr1Zf2w4&rb23VSFXSZ8)xTH4&r%Zafp@YY~@ zp!Kqa?GtgNJ|TK^$*ilhcjbd%#L6_jtHlaI#!yzQxh+YNcP9yU_QyC4hFj_`W%r50 zPu_CRAWs}^L3U7+ss?4Zc~@B%p7k5Wm-o!)R^)~7(iT|;b5vK?GCiqAcEUBh;JbVk z@SeP--$7Q>Qr8eA{pPf^HG9&o`;TVq)lZ^r<7&5xwVUV6Ln3}G?p$I19U(ao*Q@*I zv?-=Xg#}+0w-Qt<6BCD;X3S{%HtUq_-q&d<>Ho8*yq`DhAn&%C-`Xw`DfULQP%reO zZEeUr4~FA)_IfqIu~OpzSRaZ`AexCD%3aC5G<{$CESlLC{i#bOW?1gdq`NxzS+C12 z+3kB+8ph@=*=2>;o90)Ag3U?%ocpFExMr1N4+do`9cdegc8+X&6}JWk#u&9NSjD@W zV_BaPYLnaCuck%se5*Q6SK2?YUHf9CRNJlKgKC0Wa&D#VoS3$j3=wrrm^Q`}T4Z1I z=W&rzB{%g}PxJu}6>GCSs~|6?h;bZ-6e6KCbQUC0Su=5v^}WQ4WGI`RWH#aA1MBiC z1(9fPdMpiT_iIfo1W zt;G0T$loQ7`qMMovsTKTHSqKvV&z|%p#Qjq_&3t{Z7 zB80uD@-#Gb*2GJ^&i~>V;$M^(%Pe`yh3CWFV-IgegHM*R=hBk<>{`vY-sQTDfmh??4G z$2#$hnFaZJL=p?y!g9;X8}whZ*$;{(;SBwr@^w#d!+2uHX-NiN+TG??W#9P;!nKi= zFZb@18O*0IOO>H=M{i27=IXqGDGM4%ENF_MHK7^^+QWh6_WAV1Yp!(ijAI;{-+m;~lY{ln>;i-_J@Y z@5@iOS=GAziY>3f1)u8ftB|c=Gzv2PQh~CmF=KtoZcAy}=V9!lOZuiHiDJ(J6DE~V zr_mW%8^6EClE#MQ54UFO_xi+{4Ki%gvt`)lVN2|@muUsiMRL7xaT9&mU%>jZb+xUR#vWCEg+j$ z@KT=Py5`xOeYfAJDi6-TDT#u1e$b~wS$obZ|B=o?I4 zY#t&K`F`MNgZ2mxcNSe0OXt`HONn*&aP+TR9ODKCx-TnuvXHt@KT@h=Aqz@A*`g2F zXt2b>UCA>*BYUf@i`hs*$NGrXjUVZq`HJ(Bq7y!EkxNF9*8}8`k@`TfwfN87sDeMz zdQ5XTOG|m6f96XAu3$dJhtZI!YC@#r2l5Qh`DC&@8hlQHT7RQ5%Q|xZ&aC^_l=+R` z>RV4d107lcS*l4m-^MRTYA>`S`FRSDQ&`yFH0}4s0IL3mkTETpg3XO6Fj=NjWhW|p z6G$zFt0a2~-Fx~d`F6u;_P;YS_ES(<_mk_vM)l-a&RhNhaxWEsf<(bg>Jr8~+sVQt z%@)+M53XR7gm<9ypz&?GMpR&(LL?^0kI=I3Y|Mw7r^9Kx8F$i8I7XCx)XU0pUFB$- z%7W=rM_Mh(AAx}7z$AVj%K#P3d}0J}yY2ejQQg?9AnE+NtSXZbI4L<`-uN(ppA(ms z6iY+0&8LwJCBnjm9LGjSBHD$)Fy*nzrzjB0nvA^?uWptEr+?<#&F~LuB7-r+kznTM zE<5UKW5OWYJ7#Va(Pg1pGVg<2gOs6I*}GCef>fA}A7rv!^pMG7#wf3oKMvMhj5|8~ zsp(D()JlyMrw2ToUzG`M!d|my9tMs$VpxD~QO0@vcv0SpI-H=V(faXTkK8xRfySpU zm{{-VJlu`cYGW{aepsUC@ahk+)Uvml5Vt-iELp8)CwF-lXk#@#KSjs{@p;{ny&sy$ zyBE9v1pOxnDNX+YTXYX1d|48lFtK~0$NOr~fpcol&2sgHcrVeLXF_|^T+co=SglcU zH7%{E28nm7297q(FkwH4;hOH;e~I(P zVm2mW9=#cyub&UOesFJ(m|)$K*mI5Mea3tpgN{CxAMt&EiC6Sja9DU{al%?>5t|jn zTUC>GSQmW`xZvxz<@Dy0n%2OA*~E*xD&i~cU6m=$?K1|?Y~OhG%GM}2jxJtP(=4ru z9&^UDyFY2*2cMkn)FE8L(HxwzScISRY+!GNF(HS?g^Hd)-ZxhhlfbXaJ_2A78Qf5M z00627?V+EC^&{5ZcU9nij}&^C?~N|ajf{#>89##%$jgY%HLV846abmO{*fvmn_$De zD8)vt<~5RR(df9c)YM3{kRG)+17dj`PrNVgs@v$$awA{ZbpApgpNeL}0k2AAov6-$ zCAx4$F2^^|cm9w_Eo2HOu2*H;RJ5*2_qRGj+L| zr>dUp!K>OJQb!uw0HQdvFPqKp5x~I#Q{upb>VJHK3L~Z3*5bcy?$>6I)Gn53pnA{E zponK|Ei!2&@6eEOHuVNxHK6_pzw_BEeRL3umD$wwWp|{N^HB_Il<19s;pg_O8TUix zk@nZzIc{{PHTq5>HhPxF>0&iDifW}H#{$caPmlFAOL9CJ%zQkjqp2TLYlGYsr#z?K z(q!A6m?k!y^uQ|l$AZ-wM?1QSi{msKStVwBM@$7@mpRegMZJ5LAAEj(3;k9?Agh?b z?&E{w?~el=l%`C?Agjbs1YL=}pCS|=F4x7jnJ})$SxK(jXN(!w<(2F%4tp4AD>E(3 zAi9N#6X)6iTD0suq_4yQI2?5V0MJ*p?vXaeQbbi&veLUf{5_Mgp@Pizj25y7U+ET? z?Qq{#lT9a`=_z18rewYlPjX#XgiRv=BJuu*g8Va&gxG~F|HE1ORmlO)(w`c!?SSzm z-_x1I$GD;eO3$ zzX=ql;@d|_U8y80?01i)C@IaY>TTvIDojMq(E`8SPNza4xz!@}wC@TB-v@w!5W#uS zJxdEfy*8^F-ajhm{rV*CsTozw^z!oZ3;y=8<`)d>?^#ELQfk~l_dy`8Oaq@LSpd>D z1N9>lvIEpClo+lDT-s#5%YO=a^Fy%NUI*w4;gHQQ7!%k)JR`H}#0ubAA}YC)@Yq3! zJ$maTMKw~ogqQ<=EJ~BRX*VmqF6e$i(cEF=@#@LIeg^XcM-9X=WD|g2%PcTLyz9wt zFK!sZ7zaLEQg=T!0qK5tzv6;MMd2S`%pO&Vx1E)#-Cqzb`_g|vj_>&^Piz*-lR?V+ zfjOkk)VfhxS2*E`yU^p+s)m3+%gqQWY_RUB=Qo#dRmt;*EzJo_Oc$fDB8`!FFLR^% zrgRqQDLBa=Rh{fj%!qxg%mx@#iVk`z!*0{ylArzCJ;!{CU;`uAkZ3Liw>i(g?{bTa z7M5@O{31H{VJFpiR#b_$*;ghd^WwPTbq>hkVF@r&oxn)tK|9+!PY}?}tbmNH2E?!x zFA=@}whlmf7a4mD2V!z!k(_Y&C{Pt}v71OBw+yvV-M_(mrhK?+VSWK1U>7{-o^J>K za0>1NW@rOYO7&{EX3pPL6Td?L&0c%xl)d@P(>ILhA)Kug3DzGovC&uF1Bma>grV@m zVSjgmc3T#}-E#I>z}YObYXi6D&t^LQOd?KcUViy08*pVEKQIP>DmDO}IFmVmhg#wF zxd3h50XQ%ZCT0M->;HMFmm>b>qG-VDsi4;KB*cwhy+R=Wl;Gs7ZRbDv8)*eNW6FP7 z4*=W6S<}SMy8sG1r-Hor(C@8AK37|;CDgA%wgRN``{({Ut@&N4paQ_A`Xkp{V%oq) zYLRCMEgcW9ouh&5*$n;%rsX`*(zyVLc4fnE>ByE&G>pfOzi0RtCiA}`A|O>j|Kw|| zVa>_+8`>z0)JcSdl9E;k6wqFp%ioRO@AcX5CFFuy{alU4>nW|GJpzErGZ4A;$9VsO zVEqe?F$^1IQ-j_*FqUT8os$A++IWQ@|3BQlWn7ip);_!t6eJbt7EqLwZV-?XNeO8X zkXX{4i%>!d0RbhIu0=>J8k7d<5*D4(-LQOb_TI!ky5Hy7=j`)*_@Gq%eR7PE5%)tNd7E*R0vrLE%$F(hTt2nfZY>7ODBE z960rmQ77nxU1~jPDc^+~;B{(98!Sp`o^~a}Ou*q7TPn3%lv$Q&?>H3UWN`XmQasoK zt-gLm^b)~-acp3CAU1>Oz?ZD!B(Q6i*+F~(QxajZX2&fhLMrcxr$NYES3$+rCoLbf zcb-&|0t8-Dl7mmTEKg{5g2+Ouw_)G0v9|ARn^TM_mvyf&Q>eJ}%`GdUP+c((e)rrb z)K8GLo#7$WJ(30R=Kxx;uG3Lu2J@z$XNhb4{P7{rYTwMPI#&agYEF>v7+Mk4-gZrP zmFP7x4xQgOo!4-uY~wxDYKXOB#|=K@z?5HJ$*{Y!hRh-YZEsL4czT(Xt-^|bHhUqS{MDv{tZ4Vn?xS7;Rtzl-ysx2w zZ-PHD(14_=mYe2K4Ci!PbOCTf75vC#BMOK~R{@weNld=>yR|)Ic|=<;6U&1#za9Vj z=>bNJj)tzd50^o9=y=#4ki%8{UL-{|zgFri(th}bKsC5!eu{NQilHQvp*awnJVsDX zp0jgb<^+5U2d?nbi;f**i?zrStI<7Oa69!QBxO_v4Hm6=`%x4fuy(4wTqC^Uc%8@|^u4an~paGq8}j#1QDa6Ikaxv^mT zhmHC#uNYPom*c)HfxVx0=1u~EsLDZTRKY~|=mcpMGQunjCjfqsB#7O`l^~?sc^IJNm6YS<6D71$RB=jbMm&1 zq0*Q+Iv#KpWPX3FL%s{v(>Iy30%0qeOslwoFzzT>vBzQpxiNGAaTFq6keAQxU^6(v zxkb1xTQ*}s%0I(A0V}woC+4e!&Fab8yZ?t*d3M!ch#Qi|ChZ~1Cdl>bRaNvduW}OW zMXxHn@9BtYwp!0^@xqrFHdjUta|r`KR$A+e|2T?mA`t}dd zPjjU?wtYSJaIBuC%XW5ot~y}%n#obRw1@Kv%8nyO@q^~a15`kj#x*o5Z!{ED{9GSM z4`XUS=P_90RdZeO&ThlyAX-WXv6%-izu5-O_{v$oL7V@{mkI0SR%Nf?`~Y|I=$<82 zr+dF*kx~(F`i(G5nkb(Rgo%xZX@#dseRA8Sv`=-yB4qO!w|@nbQI460L<%E}pe!a7kVkr(syT%+;Dqb*2fZQp%0796ow}sHsVcn;AZmv=ur{ss=l*oq|1HN-3)stP zgY~gvUm!!+%BT*nFaY!@z0S^%ok?k)0xWWb)i%4QIjC80?fjxAm#3 zlPI&0TAVHCKHV*3q(+ZkVc%BkF%|csV{}S%J9w)+)^W9BX|ImEETX@7AS!J5I1F&` z)o|Yv2a8TQ3gipsp_)o%;)|vu-LF9|V&a~DsH*W2Pf_ZbBmjWzA48NwmV$JICtF9j zg#EethY&@~F=^!YnqD7g+zR8UG9hl^o1Wraq5$aWLQcD=S*1zE?v2>Tt-HT9o$axZ zJD$Bi*uWZyqcI6hQT_pfzHd_*td5!u;qSNS)$6Lz8Kvl3UD+ZsUmn6JDi5fdQL7zzIxPqE@HNZDLfiZ7$WW!NJd95cM6JC!IIKUlLKaq?Fe~v@dfz?*0JP+)jK& ze-%BbdTK&=#B!GY;ptCzy_zYx>#7+0vW?3tVBK4}^uo9M9bu-acBAUa5;%nnchJ#L z!sM)2B@i2{V!fO}SS-xOMeY;;;N;ocivA@(Fx@+kXOwIaj565H4XiNN6f-Wq?S|YB z7`)Nz*O`}@lTB1wE)j@_sx;|gccZ_N;3b-g)S-yqRH*7by5Gq;)0dGi#S~z&JWd;_ z@D<6yz+2O7OEH*DvtO8Q7k;$8)zj3)5jPe!Z_TulCXAU>#@=EP{N~msEbj0qc4cIM zcaF1t&--iZS_?UNd8p7OY`#-Ch9J$Fh^fe?Ugb-ejg3?@lr_oeos-F)r9;$?d_fFr zMnLinG;5gSn;RC#6_D%(XQ1l7=t%rS(VP!m7e@~nq(tf_TNvKu2DbZjAGWf>wYtGs zJ?lR}Y&0wBIVauKI=eYy+?i+zx^yBJjx& z`pAE23kIMRg?CB!p)zL?+|OOX7$~*6$JEz`BkzrAf0L&bYbh&=NOFqB%rd+l8fzqv zVg-JhCm>^x+|Qol9eg zeaO=5Mn20Unr`#{_?GX;jy6}(Lf@LVUL8^|@Rk}J%mRyf%aiIXau3Drq+q>_JL)HJ zDCYh&bm#gtC{5v`?4_@yzC0jIkc~{r8s*NdS-*5Ax}_bMi;H{MB!izm`Emi*9PT~L zQN5?yx~Q{E@kD*!w%l!N4{}nteLwcF*d0qal!x{2Oe||${RXNhrwO+yW)o2mX>LYE zI@mux0PniPonQB~t_;XmAAHx_+i(~AB6PibOkrTo>E?hrvJStq4TjjgmWQ>ZYo#b} zxUqp=u8l7_i4jzbY+wwCqd}sn5aw|J1(8E$DrQ3 z1$NpNlp|f-=gcx!nz_ZyG!e|Lr1w&8^)3B+tOad&;JBIDC}|aJJtEith@(HhgGbX` zW7NS=rbKeYEi+*>Z7qx!#aj|jba8U^KoWt|p%7Lz)}OnA2#vN7c#uHI(Nx3$ciPHW zTE(gi%6NJ&2%9z{n45Rl3I6i?)n?A`oE-}!O!XZ>Tju%{H^D}xwkWTpM>WK*D_vm? z8HwHCx6kno8)R9ut^|PRsWflTCVQq1&Q8<`gn42>cAcpzhAZR;2)9zMzV<;>)Gn@b z;_WG1@%$GlA`Ol?OSUkm^Tys_tNqv2G2m&}m%1!^@yR+O92ST35kDi_$o!5>T7~0_#1T<68s-mg^+c#9{Adkg0{|8l`!(hZY6wqpU(Wl#LaQSZFjli;G>WF1h`bZxL zmMMC+rI+8CcDALP@qd%&MgKVE)uGyfb6Z+#Dr4#+EZ^NxeSk5!my;iCPS;XUcqq7( zzR+;XmLczX%NScog4Mo@jm;+~HtTWdmG*v3SIcWAdBE#dC;4l(57;=McVlmA$$VlB zm8Tx9lSeFX@4Tl7Gw!32D1Bu*ebtihgY0)vN>(Z4nVUGR1M!^cJ#S0hfbv+xY6*?y zZw&;$^{#QHS!X-RJgTY!X}1#Gt^pA$sRW}wdLwJmZ|qy>Ur2tbR>tdV=ujQas02p* za_}IpaFA*L=oX+uEVXL<>YPYQ|wsW_@ozps4>Hd^mJx5zEk^A zc3zmH1&C_g7b7nD8rqK38KT*Wgwn@;ne=m+eOz!F3Q0o(#Mb3i&TRjTVWjGY5eT_3 z+b5Ph8wn}j8?fNoz`GwpZ2s86NEmqmLD4H9=1lmaLOs37TtAZGm{2)#R=v)LC{(;_ z(_lzi6&}G${29N&Nlk4H+EL5T-?~a}hFmZIOskPHAcwZ}P?BF1?p()i43flzxh<`!NzUlMRAb}3_mgwa!1>6ifeo2N>jWDXE8fz}!dF5gkO zmXDecRYg3L*#W0hVHUjZN!?fc2cZqEo;*W14Y|E!$HF7l0T<&1w zu5DS-o_ZH8J~!2a=VjTChel#TX<74d87}#&G)#|MpgLmJ>y0?!)Nyz11r7^^SRSVi z8EC@c<@*j<=2XvK3q2X;%Ej^uvo5-iUpl3WT>Z*T;&y2?&sM3OW+k(E^8UwElO`jg z29Q|qb|dhrG>*{l5WXqVKTy3;E%PpVz+XV#dXfc+w2i6oHOBIz@E!O2E$zNT&#r4x ziKCC%jS~~nJC8rZx#HBL)pJA;U7Vj16NEjw;@t9wuPb1Y1|QGBWXvJdp5cfhhU(@paw9` ze6rm2(ZMN`+7@31Bwio#OO{^K8YzN1hdsl4>#WFVY`woGVzeZ}OJ&r!P5_2nGbwX` zrI{W;cFHs1aEKXdFEut%LyyM00TX_QxST`HUzrEmOvZMn(i*mzqClKreu;s2NPner5^( ztREO^5w!=Qe7%CG2+)lvs;XWS`T<&~tH!ASJ6aoqlhnnTVbG%O#SxC(TT1QUw7+I{ zONo(9h$bS12e+_sF(0|RFm7ub$anTH)@o>Vi`zO_a%u4d6n|Z-oq4nH&LSsy`8oFc z<}MGCQ4`9+gQ~!2WueQKMMm?ASK zBp$9GYVe-xj)akFL%(%s;0C*J-CpF_gnYnc2$Dg!1(2&5e;_GRbHu3(-!E~Zto7Pc8@XHGfGb_ z$0PL|$sETd6pf0R?pO-1I7d-q!HYKoR~iH>7o*H|l>i7=Jg`<4fsa#$g;(c_1rqYE zY~&}Dme0a_1o~pCk@r&NF$eda5_70@FBRR%rKvETVVB3%zWTzPGOCD_vX0z~ArNa& zn`=!d*}jxT;86H(g4IZbKgDteCdHqmHd;RS3e{W%K)>wjYD0tpmK8EM z{zY}&OjMt`Eg2E5#@4h;8uCoARg)f{yr6Q4d)S%|guKP!;DV!s1)i*y(S+#gaoLY< zBE1${xj2h_jee6ocCv=GWb9b?WjK@`qDIY1&b&lxtI?Z2YI&F=r7Mdtj&;j+pWDvCVGbLjb@MMEVRsv4jE-ny5!+KtB?uUri3+eK6&5uX?mvM#*mv zyZIeLSD!qII-LLg%lzu9*B32;>$`eKBA2Ico9UT6QGMb(A&Gn8v$~OrDAf>NT4@-F zF~h5)@}vP_uvzsDW7z;mRl@FPAcxM%vfG0Ixc{`S6ikkQ#eM3yqQGfdW_<32gbusc zMd$GXXx`S=X+=vudA-@)B;4f3`wS4+VB_xHV{ygQ$ z^rhrD>H}_%O`X%ff12+nc@&1yV^m%c=2#+5%keew$y(46a?X`WXOPGQ(DH`BY{)m6 z#a<9i({#v3z3I)oQr6eZ%sO9g+ME3OUHQS=Wqojf7dV>3Vk~NVmplh}mQRNKCT3jc za}OXVJIx+!HrlAmSejIk#?QL*wS2e0t?Z>w`P}?j$!+`|Dl3H?*i~vECs)96lAPel z1VNd7C4f-!sSK=ow%o`=Gfh-Oe$ZJgUoslC!S_pr&BmeF3!bZW!Q0jFh;Yo*b++M9 zsCyUm@Z;0&Cad%h*mHD=-T<<2H_VI=JI0kqJaTr3H{w zkRAkC(b--FooOeh#1H};a!qH5Dr6}#udy`Gkyw+?!Zz^okPGBmj^*T)lu38=t|D?~ zAenziqXmbjR5rvg23I}LAigbLqG0s|K>WpTVK*B6YV78PgMQe=B8C(*T&|z9^5L@i z^QSPcrNm~XuZB#tX=a(_xV%8vAKSxH6bGrrRUdW=1AukiITkcUVXma=OgdcvBPm>{vk3!0wP&2G)Zx2yrQ!S~?$N+S9lEJ0}>=weY|=5podIw5Iw3+O0gM`PN4?ZDma zB1awaF9qbJ7^C=HD9;t*1dRmeKH3U7v5FGs%Q!(Volp0dcAJwV;H0Mv-$_pyq-~+? z3vDv5oDX(E9eRQOp_=EnfXUh1Cj}ViH&D{~+$YrzzmglEl7--JPk|4ut2Ta(?P*Tb zLMacGJ{$L9Da85If4S@ncjf z75H)Mpf~2iah>Bz{!8*idKGA~!UwciLE5GOq{(t`KjgRp0lxo#D^0-{H=r;2Je3N% zAj@Sn*zu+&p5oHS zZ-TL|L_lx1KZZ>@#DdI~L&iAs#VK0IeJ=i4bbH<4u*ZsmOvYjBcggBXQ)O9z&H#WR zmyETUW?IY5V~H{-`}~E%(6{_2mgUczg5DG0fLR96i6I|PtSX(WOyc?Le&;`CS3t`G zvO56?r_CZ^&jVPc4qW)eJ{~8mXO^8cY1smd5AggB-1jG-6jC6I^_yQ?fZ#l-u)E3q zo{K`bx}ynFCb%G50PxA3_}IW7kWe22XxnNa@%XA{&oO7jDeCi%3v|n>(VG1su|5hF zoPnx_HvScVmlgL;$M612wegY3*%#!p^c-1E3O;N7oS`=)9ybePT99Sz50fW*dI|3E zAp%=U$EL3o)!)8NSh;@-s$IBv_*#(t@F9OWgRwVfvlVqDS7;OMM=IZaPm3$V?`;%i zs}fQ_6u}Sr8%>KLCq5E@wUM~6FlT6F>6CL7Ieag_NEq|2JEuKYfjP4iLYGnMaoeGc zL0}LZCx>5kz~4<@z~gE+rz|>Jdm2z)q~S(r26t@p+4L0ok~+&nvl3lON6<53y6Nh1 zKW^zjxW_i{Y)wf3?EWM`05C)qfTIL!UIW+}>J^8E8lg`!5 zGndK>WzwxXh(MG?NMjzlpGjz3Sq|Nn`jrCQGSQxPViS)?wk%^h0FI^c5a(^{V ziL9{Nx=#5d1W(;G+Skv(I9W#$OjOcyt0YXeQ}X81FB7P0G(^kV@u3t+_+dHu_NMlZ z;qT4Oqesw|xHy6#>#HqceOg9A7Y~-}{J4G(Z7v-8{|o;&>qc1O#Y~_d1A$ z0gFchYFlSHa)F;@<&Q(NTJ7C24j)enunngsMw+Iu1wbvH!AOrf9ioDw=*X7o#_;Dy zX+wjc)hn?chkcN;(e0PB+(#RbHb_HB7Qb@!c2Tfdu4*t%BL!(u!5TsNm2e|#Yg@bP zDXr4>0((Z|1q6;?g0IM-K5VJ%E(Et8lOnFIVz;K5S?P+ikK}~h@(jKPaUmY%7$%n1 z_Mr{$0fo-T8$defPL{Aqif|U5t)5bsFz0^{5Nl|}YZ63rBw+0&)Y^WJa^xNgYWw;| zKsC5a`|6W-BdIFiUfLjyijcDL8}}4`H-hyn>z72eNApBb$gk#sZtO*ef3@77{sH=Z zKYpLXlslywdYR4xwj|3tGWM5paIxrdEgv+FktZsWsowbrfrHX zq`4~Up4^nGy}&N@;j$5vHE|2o+tkL_zoMs6vJ#WwC@KQ!K(XHN&#UR*Rl{V{n^TT` zH`S>j7 z@+63#==l^{naxO^obi@~~+Viwzt-TVzx5reEDJK~S{5IgphE=Sy>HF#~)5dSWTvzSQacU9# z<@r4pslA51bFOF)7$1FYCP_jHe(b3F9VD=><8>wXp~y zVUiFaJMn%-qp>zlUVe?KP{Y#S{`KN9rO|O*Qc9jQv%#(PQhi*vy1c`E{?gmoCS~99 z))AFiW_+sUmy6TV=9jVCs285IWv6sil)%BvYiK;VvOlR=8o<5fH_Tvaxd@Pl=%jL$ z^7I{D&y+U6>LGo7Hg+wayHsmAL3ryGHjPH;Wjr_Ez5sdS zh-q9-XdJqCI8M7Vt+&%bFV8~U?mPl^3C`ArOwOu8xrtFj_%nl7`0^T%I zjT7o)cudSJZj7PopPA~aEkC)3-gFp(u@;bF*Ibfbx@`Z;DI7Ln<4O*_GZpTsqcYYX z7G1QnMn_CwzP6^M^c7tK=44(n*SJB4p0vgmK%&m$P8ozuW5}dT&^Ln&#}Q>W%CY~3 zdh&Hi>gJa>=%XC&;9W++46Cz_-#tAK$#=jt)F2NSf|qGy*4&V$O$f2+5OZ$E0s-Wj z+wFm!>2#pX5{t_<%0vRY!3i0lHH$7x`NLS6^3E+Mszcov>W>T*-#tk~ zM8a#@?0z{@U0<^qL+;*A)N$=m`l)#II^wdz-Vzy7-kpxvI_P9;!zVo3iI8)PdxF9G zNrry|?4OzF(-D6I_y2087uu3u7+K;I_Ym1Wn|t3B!-D&FpgZb+asIzE_T}6oS8>ER zLWM~c7W%T{zSzxuNvhnm{^b9~I{(f*xV`62waMw&z2-FTA92FzMl9H>{=m}zoqhgW z^8+~j|NYp1Le)DHY1W~zQNFc72eJ_I|G2Q6w&1)dEZy8g*8pMZ2ZTlNzbq``K!8Ni z1t}4ZxL}ZiMkud#c$h@l(G*0?Z{^wfp7pHX{k{xsW19bdWxk%$KsoPlTo1@XJ z#umYa|HpP3bl&#|DiEmPF)OmkVvktc%{aG_Cq+d6sa3jMc>!+lwP)J0P|oeW(1LZu zwxvtuY&FoE0Pt2$nnza*0FDvvZ|-L<;Kb0L_->%rs~x|E-V9%bh>ir-ma5PT!s~=^ zx^E+{MV%gG zwUhE#+9Re8j~9eZ3;7~2D;2IR%D3_RzqNRT5z~u-MS04cuE5P z#x&B*`}#Si|JRljgnJ2p4a#PS_NFevN%}Wu`i~W)+}XTcKDMq(<_>qOMzyUs*t4~# z`y!7cqsJM*=_*5NI@e{n&7WlMqJmdhm{$>_KyhCIgaVQnel_F^S6JVDwN)v4GuH{o zeP*j_y7!x6CYZg$w;L3Al-3$(-lUUb1@0C2d4s^x4>xR9R`-8^bbD?w&{pRz9(7=> zr}=TVQA+Q5eSqICfDVD9oqCy8s@ikL%rJ4EtYeye_~ic$l*D6^#@XFE0o$`oR|o6d z_*7ZeeOtM>-|jnpTI8dI$aQ6-cm?^6-cPYp3_1y?=ake2*`GLZqm&8@bo&8H1K82^ z)niB40--8KAlGraI=A?7bosPVxHj2$!++@ zhM#@PZ@1y;QJb6|%x||LU`c>x7$@P%`QDw#^x2>x!2iv?_${{i?WX;&jr}v``TW~v z-3VlW81F&GV<5&uMSSkJLsHXP7tnZ?f&CtwHfB~9`z9tFY&3Hvn95<*fF{vX(~~{S z*3-)R;|H@Hmz_|{4AW1+Yu+^02tuo?K#mxvh&J z6(2pOc*8K1w<8r?aWQ6W-ger)>v30S2Brs8fL9KT^8x0RI?aQf!q)~;$=6wNm;eWRhpGb3utGJbyOoxf&YrD0S3Zvjho#vUQBAWYU4CaqsTz2Ad_>Rv z*(U~}NlvYIo`wXq0*of12@R#PdgPjo2dc5dL#BD;4~>?(!wmd{O6r2PK?B-~JBn<> z0E9O5vc%Z>TEYr!6aUufPk1%Ji>yk}+o3I`dK*oNBAN1_%o0N*n@hIM`#4$-v#%}j z=c9D6EN^IVgwJO^AE2coUHcHJWlZXe*5EQgI0@NDb5j5cuWwZ&UdLxTedYEQAWk2B zkYner`-pWo(ObN-%g^i~2thcZc??Y}<#fyJn122tHW5e_yoG$-j*;$P=qL66e3hOd z-J*m>ptnd;6u|c0P5>gDbpX0ZovYlYAxnc+o(zB*Gl&45X(Y%g_&3W@h^DA$T|CFn z5pPZ2@rDdoz{~CZpvRV(qp=ez50#-|;P_-JfD*ikcR)F#HCGQbTbiKJq(qzN0YDDk zGyNM3DbF6KN(8mFEBj(KtAK*);%f%%QB86e4_55E)B?J3@KfsBGq6LF%`|d%>=Li) z3^Xg7LSAv08s4wx8Tn>ROnZ&~yAE4|dS&z|4hA?6TG?D!5^Y>Ch~as)9qwHRs$KC} zR`Iwt3Zyr_&_}Zt$_guYy&KXU^p+cRd5>hFZrHxo$UE#zOOp0+@tTvX)Sg$?$n@{*u!L{H*7aq)hp&v$N zr5!E#0GSrVZ5Z!4J~`&cS*{IvqzUJYzDJ%INgQtYro|83Fm>1>f{GG69_ph$%Ct^1 z&tjQos+y*-e)+rQr=crDAFw~QjD5zPHr%H5M54pz_O1|e<4tsr1cwxjc5p;FI+8jB z7>B;*zr4g|h57-X)v*rqN3oy~2+hLpH(uif7+0*S|cJ zC!jKQRe(o(#f=-PHJBYL3R7JL=Z{LLC?Lz_?!LoT_R#9yuq^(%+y0!7T&k#?w zUCGWZVgMFqAGL0Y5Jv%|Kf(_YQ7>s3HOZOv>}BJtiJmQIPI6b-UXGWz+1Ec?QAeAO z_@n1%4>paAXOdsW&=fS5>f?}Rp=>PzPhd#0kH>jUgx!aF)jUn!X~$+m z+-fQ&$$i23dhmgUL_)?5XBskE9YZ#8vOw26j*8(mBjH}s>lP2nx(Y|A2YnV`2SHgH zqG`+zY-vO2(p6MwVHSeooX|@1eaJgbqmAm>=em2z;+b0W2Rb1ci`)b;9(_A3H&w6b zm#RdY(p>hR!w5qnZjewCCNXK7NFKn9!g#)M94l%*woKvl)R*!OR>S?8K#%Uw$@ey~3MJ53aMkXSQje zWVg)ih?hf4e_LvCc(YzRWq1oJ+0a>=tv+9-8k(_aJED#yLh>2<#rc-E?+9Z?Z)lI8 z*_dC23IQ}r$5f5a9ub}imk2LP*^MZje0o2WgwD>WiE82#+B9b1etQ5{dFCFxR!swJ zTE?_BiW{u{p)}ulDv(%tNm<>1!Dm4Z)IhI`wJ0XNE*!X>K%wChs*f4|G+i0fn-g-mQtSzf(ceFgBdXeBl@ELRYBh5O*`jke}far9G-wtI@W-*xL= zR?Lb_^JgX%nhiEveWJM@?3a6`kV!z?*mjahR|134kwoiWhd|in>k)5xwaDgz{CJUJ z8tYk5_69x{FUIXdD5Vr(nf>Bk@WLe1L)Ez56?VxPBG32DsO*~P)n09C9s>ahNBOtpo<8%QRoK2T zL)=!v4ZEpip$y{`v)yRhG4Zs)VbM34W34pLuaIvrD{zy-&Eo36^I@yd1LEW>I%anv>m{+3Si;v`` z!$U7c6-Z|AYO9S#te4SJ)ioDyQRD6XYvwD71JC+XO)wjraUOj#R=^7{&tY$8C^+voo^32HJ34EV zn!I8)P<(G}sM*{dOx{-LgM-)QCqeOo0!yL$h$V5JTiB#~OOUT+%2b|fsEg(~MG`$y z;NI2TNcz=3rhm1Z`}pnKkgMyf zoh^injL}GMnov^_H~?3-nLjz^F1k-VZmXfJaM_8Zdlb)i_%ogqSLSVeWfu_NW9IIL z9ZLFj^Bn5o+)oNQP6zlo8nGv+ul z-FE$Nk!}{if^o;~+B?|;qlXebS&J%f)DCTm4$Zz#UXVqjW_PwEgR=)N4`Z`?^>pWY z85Vo@iRTqxo$;3#iD3;YcQGJ)3RRSpLDhU$#`SWd4J+0BhD4pNEM|*96d{Evq8FUN*j3W$zU8Qm8R^senrJ{1tfvy8J0KoB*d*b}t zhx|Wpe&ORfisvn{Fv@#~?-^%DFOK+R`J20@)cKEVBNw2JHvw(b1hi2lI^eX@7IZGZ zr=Rg3Y5hf!xNJJ-|Di73!w7Nnc;Z~UN`8P;g~xP{(UgH6tmj(t_pHmsd5{bDCkSsO za0%ecIpU~hjhXl$TqRHJSXSBv@{<#>#o1p5+z8-rDL01zZv(J;mrc@M2n;Ja#U2K>CcnGs9%_QbJVPaf!}S;h|ng z^X6LBv#)a&E9JKBe9tn?qUC(DWbuZezESQR_p>c+F>O9w5}_hMMW&)m$Ilf9iEe62 z1%^pu&|tC^TteI8l#%V((q;@#)iCWhYcQ$JcJB2F*A18i* zFwK;-o73k>IESNLg*toS;x}WAhexIMi+4KY`qbptAU+)MZ@C<+*n1A$D>uq`$Mb!; z#TJN|B`356@tC+!bEb9AwnnH6^=Jq1t$sgjD!{~d{chq#027~dZsLXK3IT5n&>cvJ z$;p-SyM_OG9|3ti-Sz+bp@EKppQ`=c?3%>x_9sn~{Q!OCYcDJNS4Z_bvg(CmceK=V z*Tw8^b-&neZzyIO|4s+1OBeqB)y0P5e_6GE;!OWK-2Q*Ka-aTJ6aYUl5TF6n0P5U_ zKR}0fGR*Fr<4XUngSk-bt9bsF_oty^Z4Q&-QtwUnm0Z{4M+kGQimh$3VjN z!>h=|Gdd*Y!arq1E@nsm>Swio<;m1mv8loWIJ!d`Av+`?KR~j;qsGUdSPB8siYW2m zl?``E^#K;BohJDCYgf%u#>m?!QSWDh`eJ5=E`=BB#o{{6Rd5reO;RGw6PC?pWQrS6 zCJ1Cb|0LMZwn@R^!(`eQ^N=$vxVn4I!3DRoH!1Uq>3mKIOLRBme-N{x+J zx-?!SjMUV*NYmo<1N553?i+#%t_eQqEwhNVtEFbyNsg52-v3~HXE8i*fc)8UO4N2r zyGvBLAZc3+!ovcx3gAZ zJamUGIa`L$vn!v}AC>BQ80r%QK|E1Xe4|#MnyMQ103o503|__2=MQwP=n_uiTGrkS z-1g-NpuG5s#bQSPIsIkQg_*5>fQ)16s&1~M5EAb0E-b?2;wC*xjACDr{q_UIb;iZXnex#ha#Dn`K!`A? zJHJs_qvf2rtUoX&nj>|Ljq4{)rIf_-;KZMqPfvpCU;M5o_31gD(?$Z$ER>O+g2Hg7 zx@v4hX#1R*5C~dNLq4EY@=ugee{YVTSujr^s{h!is=bp}Q%61(dcBYn*(B{e>|4mn zf;2I2B1)tc6dY1xB3F@X*LAktX6Dod*>tUYZy^S1cmjil+y!>8Gp(kTOnj~ zG4Z|}H~yRjLc#$XP4RRw&e}*iD%z&x_*YX@4%iIsvW43`+eIsmqvyi-vuRfx8CyOO_F!$2!Wb6FIe&SIgb zkVEk8Q)vh_v10feIiftr- zXa6BbpgIwynDN2`g=)jd;7!P z8WPU<=LG&B@9Hg7GwuGFtMvrC{xetWNy*NyjM4q;r(|hotZXL%>(93J1Q9=q&$W(g zI3p=(36$NO{>sIwo1e)4{!I~8EM(YuK^(pUTZg6ZUJU(C*M7#+c@i-GQYHU8>H@Vy zk8uiMvq?&V0T{B=iN()AW`D7B|IT@&58)q*Vm9w;?!kLqfuz7$7((%jJLT^j`{Z}klp+bgpX}B-q@J}R&rZk5Gl&g@X#EOD(h(8Nyt%tlnCq_)-^M*9? zw&56fD+{$$c?T_nWW38&(EKZY0J73wB{*!) z`KctxYu*qbkzqM@4l>KBhE{j12vYte^5E8ix&ZL_U5koalOfCRipV7}$k`^X=cM(F z4Yk7v_+wKi$E7!Ike?MEsU*oy=wj-EhlKL8r8DL`IeUF~5SsoLU$ELkZrmWT0Bw?_ z_5|t~Np1Bl12N4H5XR!37Oq7zP+F0@xSl`y6UUzOYQ3OX9nSbxF9&H82OJrXz2hF^ z08`n6I@3TDlp9gaf}V4n-?ud2Vaf4zXXe0+>!nAh4*Yz+z4-3jA8%SS)mTSd@ziTfIVsNBd!hE~ zqmZq){VQvMx9yc^*ofLely{ZV$b24-yF~pglHv104E+M~`V&mJGQ|Z|y*fKgavOc3 zLah?4)lmcbp~XkF5h%-_Ka&?HOk~)}K4pNitM$sgGthSgSW!5?+a588FcdUM@`9FW z_H4aLd(`qgjDKOV=RB}`4A;keC)RHlWrFW@r2B>Slafn-rIv3Ff4PTy8+|ztxBE&n z&xBPX%k{wp`P@C!2mM7c^IvC|EpO+SM%E3#eQguDp)Dtg>V3u56R941QL==++wEVu zBTIP0EK!}NYKtS)hF&&=Knwdn0k?Il!_)UPY_tKi+3lrhEo%HOWXNH59yb?Y{hyp5 zQr$Tu?TZ@K1is5E>f-?h&a{S@=qvOwB2=E5JUsQ}Pypoq#^oSR_vg)HH=Vd^7pK&) z_@Lij`6Ur_rud_~;D(l@Pfxo8YY6~w_v*doRDbu#eZ~n@d7XR5!lTwvFn6#5DA<}A zCUUfZH&~Yw@yvH<=s9?tO2wITuI0JzktQ5aG{RN;z-yV~F>z^m{8nRy0QG$u5mF2N zFtl5eV&P#|+@tEqMJ+Ms`6>=j6opFkL!=61CO0~0{WW*1%Q0{-E7g117TyRvN@0TF zG+I_r#KZw;mVaeWAHc|RLad7R2#DIZk=(I2zukB!ZVp|=%o`DFYI$g&gr+BQnP)1U zQ@=tTDw$QEs$-&B3?tmJ9WjL-f6!tLyn9!O`nX4bU}fJYmk#-EIm3ww^el1!+Fr}G zo@&+ET>%c?6G=CV&aI8Z-qqR|T5+|5a2m6{7Xht4RdL9ln;}N-9WYStE7bdiATi;2 z{&#NEvr%n@Fcg4rp_90Ar`KH$GD~i(t^ISv*RnSzvQAJsNh(`h2B58dp%I28?hPNA zU

(Vvh)}A}5aPvj=Y2dS2=4T-U5MaDE=G%xz7m8N{0N9y<4U_lcJC&8{c_v!N=| zjAEzXd0v5dp;DQgk=(+z3cJ(NZgJUCwV$?_FFuz~hE_EtP=T82!TqmDHlXI`%+iNR z_&vS9tbslO5dLyx(6hZLgrNBk&>{@c)b}Nh+IGsm-?4s2F%9UYz`y$**_f!|o*=k* zohoXncpoUG(ryGw4}sdIGZKi&+~n0`2VSs6mrR|;BfgaY$JF5hTF0v}7q3zxvMkK? zH}`4gpQuH8F!dI0rfSKjeoI2EXv?)b3v9%FwZ^-|O;i02ZW|iCkpBmR#u>o{-qN$U zV4XRs{=yMWrLwi%9k;FpDPQr*h|bUSR3cPE(Ai5LZPF|mc1x#>Yc(EdV0-DO)XYjP zKfS(6bGgHz@p)`QL+<1s^bmn(^c9X^fIm7h>1VEpWa@`~Yr98ot%$kp?B}Py-w*S9 zQEd`6_h=b0v3<~AD8ykZGbt%9{>#$wQTwpJ+GqgRc~%sDPJnR2mjCNw^G+o0gC@ur z(BeW?MkNJz)sH#BHRR0?klo>*>ZSood8RWug$t#vCvf9m*5v~UszmrN>hgg^&adn8 zPe`o)sVOoS#x^hjvzNmHBxIta&g*+s`%tO}qnzz%n%yRFcI7MS^5Pp~_Hyq-u8quJ z(|Ya9VM51EX!VfWc}Cg4Gv}U~^{@@(g_Wyf)tYund`7aF4t~#8CEG&x)b8XRg6jg9 z=w-OCo4?lzHkvK|M4Zwl3k_x{c)d0nH(7Z@Bt+V~=K(cWxH(++;lmXNy-~F=_MkCG zr*^(qd2ST_e78dgLhN2=%rp0h34Sr6bHthr08OJZHind0Sy}t^pVE(<$yd*;L1}xV z(Il3PN>AN2=IR5La{`(ty)=0AGujv&2L7hFLO1MDc5d@e-`8f8XYR`iZ zmy=SJzh~P1{+eYJHBNhOT=eF#Hb3ECUpx6G1* z=P=9fSz(KiZMgynjz4&>&KXZ6^>`kS^Kz03U+c(_F*nKr-w?A&ZGIFwBm<&&?ETrT z9}|i8z2fGZh-%5A$)IX zr|oyrfIpEQg|I9h936P~902}ZAPdQNr9~l}vaYQjOy)(B##!9!qGhpGSHD+9bp2Y3 zB;SQeMgEWCbp>d8^WVO(FhaHsj<4TaN79<@x_#ytW1JOlJ{{C8yU(M?6|-{ydBv#>k*^%MUC>>4c&Qu6BWy?` z259aoJPS<+`V0ZGkBu!Z792ShHz)atj~JYdTrG1JNR(l=+R_;#(w1`^ z3zAJLx4vb{U9v+5b+~K^Ufi<_j0h&g%|j+G4baITe$910)14$4>C15ufvZ+khMo+M zHVUm{ms?(7c!f#xf=u>0YB#|Bk@7}1kZcYZ-^H=W+dpfvRX%8OtCC^Yz}RGRW2zkkQ)r?QnRm7* z1lcLk5`8hAuqUM}O17dha)Wd}HM|{8i*OD|yuM5psXfs_v~Szf`%9XRG|1w?78a0d z(cOgf1slt)&5RU7r%2PnkSRWOky;VWIKZ^A~dgv({7VUQc|#pSxXt zBrjZZhve&-&=J-k_rWNaq4$gXc9CGyrNP;$s>s{<@b(g>5B;-2q+0p#4l-E+5^;a= zYMOcf`+fPBZWQuZhEBOMA~5l{G!(tXaC;SfV)YVbji zM@onB?8iCST;ev-W|bF(+!A1hpl5|D8|^ir;4X_pS;VNkL>s^1-+++Jav<%G^(7?b zwVuH;i5sXmoXq~_$@zcvPrr~7XRJ<24CI1P9~mVjHX+O3=yY)Q*>+X_d|paxPl_)C zY9ZW_W~A*TW|FA|iDDNNTj^%?yEF`vz-}1^4TmO1+q9r(BEvokrHJGWK9;zg{ZZ0+ z!dub9NcCJ_$O~D|hziRzpSPj&yR&UJR=vc!b<#mh%)1J?K;}fS7UzoQU6BegQ6+v0 zizX5(7)DvD!AOGxwIxPiya7^)#}x^&sPh%Q-m-CSm9oWzITxV=_gNW~0NVmg#RoQ* zzdp0Y0CO26D_`b#(<9rJelw?ZSD_ly1i%00%B005X=yR^1|n?#J9J4Igd_A{*wp&g_C9~* zQL~QjhXj>TfFZ#h=`qFb5WHS;uKG_N_#(Q!eLbKBvT>3-hO5n^efqH_K@7Ar=sqmc z`Ci@ff~G4it%$GYk+R5|`@OCjOHg6?q-;3yRL1Iu25+qHd7PgJ_p3zTG`Lma7Xw3R~}Y2Ccg0U!k~g=5T&tp@f%gY&SA?ry7yboP;<+Xq|ui*B28^|KV?O0 zzbWv{u4t$LMQPF{Chzj|uV8;Y4>YKlVx}vwO|P-ZpRRNA5h^q$@~({1q)ixS&=P3( zW0W?Q)IkpEp@`|RT<2q0n(GgnaOeMhT&|!7J`k{uTCe#ITEkXM$w&u?E5)rlqu)V0 z7vnatzl^Sfw^&P=7Y>#;z`fNSVuS9n)Z0oN!%SCa?TQBaY+QPKJ08EeS@0yf9u@zw zng2yI zsv_@K2{tD35WV8Ej)cO3(!6}~@bs2RUlGpBvhK_Mx9cd871e+ZvP^Qs=G;Ck*gQEW zxLUC&?T+Eox<@$?(#A%^5Od?I-RTv+31d|GmzYm6glYC9q#D8*gv2e zEyU{gzLrAH9B#5(tyS*o9KfTpKbe^N6+B(!*#4Ccx3s7{H}uO*%@J+obGi47%#c8O zJbogHP;MYMM}{1}${;r#+*a^f%BRo6*q^aq`9vqrWk!u(>g}HaROK(v<@4M-$bcms zAY8$bfIvryzZ0f1gt;8rw&mqc1DsZW_%lre=+w`DEM){>5c~t;ev)|NMCTX(;oK=% z(7EI3q}*xv!8w&9`TNn&6Y^Tn$+dnF@0>dpu+TZTm57e=V=K=R1iKo;Ng3NG!LV=1 z!+seY4{9EY-+6Z-B5z!m{SuzzLSmT@_f(x)zutbEx={M`4r7itFyje|g^Y=*l@!lk z`)I-3>?z7>ExYGL`p%hUFL6=xa%b_{L3!(E$j(8LT|{2uD6uqDu}ZL=0XmOJ^#Jg& zo372?jSQ8x@|w?{etm>YGaS(mQX`2T<>lg4V0%w*95mRLk-m;D){7V6#9w3+^7Y+I z7T@Ztu-EHT-{=vUR}AeK%QJ+`lbe9;jIdPRce=I|A%td)6hMBTpyz!^ycl==*%%QK zN*V{9KGN+ATAv?Fhe-AW&XxM>bSfUM;0+FV^n$vwo>J@Uap9hX+fWPe+bc;A_wH$Et9oqg2LrQHpP#Z zn)AX;OcENB)SDRK*48n|IB|584TKyF{?*yN?w>>zht2D3%q#O>TwPLfDG?tjCTyLb z1Ji^lW;xcX3EmQ3rO@o(_8Z&O&KoGGtg#xp$}76E4aKbxlh5%W*k z9w5Q{4gyw>s~_$1y?Mk(-Xe;@-$7RrL)*8lWD=y&jr`u-4x2~A@7v5H(pw0BT8hC` z&i_M8mC3jT9c2S%M9%4~#m$7((3Fp>&PGKHUoe_k0?$3h^F;Pr#l6JRfW!2h9eWhn zu!!0z?bG{>FU7ue+*+23L|n;$7g@z8_KVcb z=ix3ILL*>r#%!PU6Xw!A!+KdTZpx+i#!>^_U=X`|9PYodD{cSPl3PNM_@i33zYIOB zNr-TMSThR^6pq|oH7c=hu9}7TY>`^sE}+|)$;OR8&z{hZ%PIHBvjvQj*B%X|M8T2X z=Qt?GYdAS~GU-ikPixmgc})vgcAm*C9PBB*h~W7EaSUZ>pa3i&eO~>#BYfXkpL2 zEqJ#p*zfb`!}hJZ+Ck#d+?-B^HAk$3BLC5ycCbj0wP&qgoLNdClG^(`_BBvIxJ4OT z8*GGpDK9pG;%&w)5B3$nBXIlOf*0WRWW0WGy)Q@VJ4j-uZ@iMO7^ZYkE-$}K{q|PY zGRd2`uY{VLBH*yP@N497w-?s+WardkAIT6!V%*;0@PZTT?htaL7fSYAXb0n`>pC+A z+QFG_(l&y~CeWV?%rrk&R1qm1Gr3?>_qcOy*NX|N)XRW|dx;%KA2) z##({x&H?GnNik*Zj5F9rf%?)oc_7W({ zGaJd`jT;Jb3No8-E9x806?&P~DU>d$_20*VT?w{@MUd?9J>IViD2u(k>u; zeqT4xTO36EMb_GYSz#Lt8~}qhr1a|eNZK+ppeBpYlo=lJcGN*wvsGR)^UPp9d?1Ej z<@;P<;r3=3>*sm0jtNMZTl3z{$zDQ#mMiVYAbe* z7bMwmFT7{=Z07q|1uq`(IkWkgLhD&t|H*1W;u zokr`9>_d+_7%2*K3+^vV;MZ#%*ju8x=c`6lU7?kt{y3rQvVqTNyufs6umpN{l~T9= zBdf6B*ZxPC!JM;nX(>+ygs;mT8r*-HbUwZ9n#o;-rqqkd=jK=X zi6Sq2zzF-5u$401(?jqkSHe18ea4tn-(@~pSIc=7lU7vK+*{gaMrryS)7`UJe}G=D zdYRG_3I(=C#x@62e`E~*6Bftz`SclCt9%BENO5oyjAct(HV>e9F|ag?Qd-&rrK8YmH1K-fNUR9=shym-x6ZaYoeqz2(&r}-2_eabA`L=nr7A|)n7aY zus1$O@WvCDARvvb12(jNhCBX{hWxI>|Hl3Q>1%gEi$VZnGIwj*hNsj(nf8i>{$UCkqP*h-` zGiph7ruCQ6ykPddTA)!5S&VH*48%e(69L;o)P6K%q|{7`pXtE2T$hLL>ZhwxC2KtA zDK}6N871Ng04a{1dF+=P#H(g7`kB0eA9Pd+u;ioqG zTLTT_H-NXF7~W^{w+>^0yU6OT{ZFn;vn39(gK0Qr}j#EhZOl%bOXlxDZ< zbtuSup3(-Tru77EQc9v99xdKaKLMUIS zcwR%c1G-mbs+rANopIgPDaXk^{Bl&%W^Ld(DPGxzp)G2G^0K0$f{sjcYqFrp3ybmI zgCkDJ%oPBeqW%%a1h-RVvqj^#6ddUIB~%ch&Ay=_&5YKz-!T^Nn3eV;ZUX`LudK8m zar^A*1Mh^tveJHpv&^Jqmhp{7N3v-Yy0{Vs7bN_YN8F z_V#x2JWfpoud875qT=lm-EO_@?HiTn42yaXvrG;Ds<%;&fadQiw$inHT>Kh7KhT3eg;N85 z7<`bYKbf))=~(4t!ne`gJ;0i`eq3Bg;GN0^b_Ls;UqiRi!O(utKkv^G5JVZ^jfT@T zdqCNq#>26nv&zurk@Eh0870Jp-;x5q>MrkjBilAuE6{^wZ*=)5BVaBng@h^MI!_4B)yuoiTV3v3i8gYM3>W$k`qSrro< z5Lo9_FU9ak=$qaVjttYikOz0u@-c1bjYZ~Lk8m8xysRVq$W&*Y5!i7TO17CmN{PSLA6wLwBDRj|00zz$Lp181W*L4}G-$ z;&e(?0P>V+H>x|kWV(DR1vVszDB)$*0A}=VUZvKUD&4NNUbh{@rjwG&{z~njE13f% zW5-7Fqpmy@nEmp^e~)*|IWe8iF^8xT6m_N_z+uobk|cZ z@WT`NUmWI-0BOmuAcX7x(6tdtihyWVZED{XwvW$+%p_r&gH!v$F@9ERKUNMuHe_@H6GnrD6Ni66EWrK99>v$8r6 z5|$kgtb$Frw%c6v-3G2f8yD5L$KtJhh~EXvs@CVtn_kuNR=lK!tojFL+)p*?dk{B7<(Dz^FLF z%5cRPXRneb_w5~{DJxUJO5I&k_tlupAKmBHlV@^1gd>6PJg*AOUYM5&Z5es-hFC@= zC#P#|fJ@jmGgJAsX)W?~VHCJmz{kr-QF7Nk_4}_J!HCQ^AKN7tc3qV z+*=_!hIeGi#22`2ScT&jZYUleq|7~zjfp)O{XBmT_z$P$aZ|#;x$$GbKV3NTH3myk z@+}g@)*GnnRgjoDWlnGp|46_%hLD{C z8;Eq&Y`o?()P|TB*ts9d-u}I?bh0`)-65xWC--sxRsjCpi}&odKgYBl04qPtCKHq@ z%p)-QDY?mX6?4Ho;N<1~55?c(RPm=myDWSeV3dcx@9ke8iE*O;7vuc-x}EHU9RTMk z7@yM7iY4IoXs>BcX!qkWIOU&uq2R842W@Rf?N=W_mF8GvBC$;Gt!h`wPd#5ZEZunf z2d3eVSLz>N2Y@5!YgO_>bh~?MqAwbfxj8hL2i@5Y zb9~{j@?6;7=V0X`WyC#H`2pY#)(8;^ zhiwLH6mz^>&v|+bE{w>R9ub69J&IC#<_R+_Dy)+eAN((Ty&N}QFYjsGKy9ts?Jmw8 z53rYkt<#(L90xV%Uf}Mn^YD$fdz5f_IG<9|tm7bbAYpzZTB3 zx}Q1`=%s-{nAjq070Tpd(TcS-SCc*pMpqLVVfYq{YLibdxiQWyg6H6AGY)zVnpYd> zb{pv{eM+n{C)uvorBxSbOH~48wV6TG_umcp+|?yP1Dx9D;rgldBTY);Sl^R<(=URq zJ*V6^kb3~nAKPbtY!Vh?u>T^e%ZFx(t{+0M;5?Gr-p>EBx^}H_B3|ut@7?u*i?%?1 zn*33fE9_F3H4<;Q7{2U29>-i07Y$}N|7zw>x-+Hn0;ip#ZM-wnBy&EK%~4v5ZJ_$@ zHcg-WOpMtgNT%uYN~t0-bybmT4X-XY7Q~V`u6l3l3p+xV1Mo56D)F#dC<6j{6MF~z?=Xl!Q%BNMsk_x5(vG>XY*EIU_0DfK>& z#S6)3$gCce0VC{bnm??2sApZfX2GHvsSJM$TlF07@~!9;gL3`S)*Y#V$^s!)v;0BF zw9FNvH+Mae;95P3!J*z^V`_Se9VI-yUV;0)-$6!$vt~86@o7d5J`9RqKWQ@7DJX7l z>ZKAaeA@$MrdPqz47qSRM7dPO%^|UM5PPfo%GlK@3?WcsnqZ7hpdHeyFY5Y{#ARK> zT_zUIx5ngO>ASChZ3gw1#0gcss!;sz*mvby_RjBRHsueD^ObIFFP0BYYP2<18L&&) z&2EaIuy7?mYK0qhGV8m}nFv^8SEJ!NzuwoH(;%+R3z- z4mAQ@Hr5T1Bg433jqPoAcRfu8dqDpMenD`x(r0`&iMReAulq2uwSM~K`ygai*=-n8 zkyF)&vwbbyM524|%E;y`83{O|lMP3?(jWbziUJ+ty3d{39JnT`?CP}(6(}kIv^Ntt zdVPs01Z>)o=FF`;cB)Epjp2b>3zzER0wslc2tEThgY z%0;|Va9O@p9NJ-5W%bO>>Jx>o3By}e=JA!nhGr`ZyTh|JB)?IOt@V7s9y7E{p+uT$ z^Gc*`LJZ-kA(QP_J1&v>?rqjO(%brss?!oV>aIn-oUM5DK7-&;4|ea0I-@o^npIco zPZoGP*C6gWr4P!}!|tV|gAekvzZlp>xTG5yv!?ce`<~Z0^cg4$*UvJ?d3ME z!VL0u$|4wJ-UO!0O%Oa%T0G}8MM1QzZasq5g)1YznI~15Z?9v;JqFo4gnmL-7e6Z z-LdHgcF&dsk_{CV1EftaTVuyT$y@jyiBbD=*>%4$=c8+{s4s;4$xb zb9U?n!C@_DWPbArG|KU3q1nUSVc<+u{hTM_rVLpKZ&rIJ6j%MeRc(rUXq=RCdBNCV zRufraoST^E#pHn{dzFl!Qp+vgXfJDAd9ZF*;G?Tz{cqe)2&GbG4F#(0ujRaUhL z6M#W8MamcqEPqz?o|fHRYEr#$qgv%|cwQx5nTM&-7~Vkm6ap%7QwY0>CC&5$eM=qE z{aNvEbiP{#CRc{4iY4rm{GU+m3W^hYrrr*$M6px4{ed+!FxYvm`2nIf$awLQoAgBZ zebxJEn1dtMmpAVl7BYQc>s)K5R&BS8;@G8-&n{odo^%Wvq52v=#w}j^97g*DkQ0j}mUN^dJN@%a71)2NDEm`ccJpL` zl9Ub`>t)(RTS?YGt&0Pl9LIDbj>Shn_@w!ZlxXb=;ggMQP0_I-v<90zKfuOkpWyo-DGqp*#MD`AZiDY8jA>pr?rNw$CQHr7;U+%-;SiSVj%D0K!} z$g8@W@r_jv=P3qe@9DYYy6FUB#Y6pEG&%AvR;+WPjD6_|Xc`SF%>4|tAXZWXRAB5q zxJw3#5|&Al&$#7V+N+lY-;>&YHM1siDj8?kNFRM)MmR7_5V-EOrZPY9u1wWxBqZ8< zf~S^{SjZ0_<@W7WcK+91_l)PKOlqsm?MntV;#*BzY;+A2@wNS8V2t&) z?%+-D#zl=zqtP}-lju$a6zAJu_axg0pZ-m6VNGi!xUX!kuLz5rG|!;YcTj_^ho@?M z15l8<%rsR0`dG)9cBY(hDQZg z`a&@zWj2__Bs=O+2-gndzhq_Ch z{aXPwkGI@6g?h?!%X3ZQvT|~ES)nE@RbI6tAq0Z>ZW;scK6*3TCeQN-q?9IBD}|0Y z4BXU{;7y7XJRdHnX}aq0t!}baLz)&0SKX!C1I7xofAPd>e(R1XOxRY>bzKI_#$kI`U> z{n}1NP5(#&eO`BrlE@b{O>4h+F?}83T-43R(PczD*r#yre1VVk zAejDzT;oHwdQgl%Nc{Oj8+jpCtEr`>k52mw7iy}({&JtND=X?BmdCJ z{42%fGpfmnL|@gdos5s1pk@0-CxcW%zgHOgLd)Ioal0OcQL%R_eJgv-`x^&y_T4w$ zl>vt=j`>@FnugjuD8EK4KQBu|MG9hjO$y44H||xd(CK+q*xYBhp4ypyaUH6%D=va! zuz9WaDH%~pF3L47Z%(~?6)lkXm0)1Kv6y$59VdPrwmV;5n=Rdk-(S_~aF@7wqN^q{ zK553w20(N*kDYV4@*!fn25ds;n_PQuSQ{*nmDj#D2O+%hnmuTm!u^hTZ)_rUz&cOo zpxjRR{>IRl=ut5{%7RcA#DzsCm{I`Gkzoy`d{vFcDDoD@c&b}kr;lOMzJv+qbkx<1Y;$O$r`Gey83n# z2LryyXuEb=*gFczcQ9qB%k|oq_*P%|qV_Ih^sfJXdNj^$pFF}0{h;3JP=|n$HYqK$ z{72#!kzRoW(8wmlD6yvP6*(Oy-3!9#7F{BysRYFI3O`6yqMdoN;e-GtV>e2O11_Yn( zjXU4=?h+V2pCyQlg{2OFUotdLy~Dly5-&yMZSh;q29z-;5orkZ%QUC_eEWG3shfie zp~QFNWXSQQC-5&(>RhQ8F)Kmx1ffa%dUc{tj14qNdw&jfpWBWa2fHTA0N3D%+||Q> z^d+{!kuUFR?li5F*1~G@4(CZ_6lU(n@QV{hN5L$xy?g|fI$`D$=3`a92LqrKJbd<88(c614O68cybM`3zTVwFFFW{&IKe4?IUElxX z;5{o6{*6cCSObGN8*lcT#``;6a@zfV6ia@hdmY-T|F^#UO>s`<+($8}(`qR>|7`#@ zEf=?)qyAx@&f};3=wJTlFaN;F@fVb!d*Zib5w_i4GU;Y9L`3gom!=z5yYZ`ehd`ZmK}Tk{#7Ina5{P* z>wMOEtMI!GJWmmcRT0c}!X5B=E+FX#LXG=TfGj**%wGb#q1Y##A+~{Cm04>E$8od=xcQgl-J*3K&Gz|$ zgS7lQP+q!LsKN^K9(x zL*3nb@U_nd@P+b>^2oA8su}hEVL zB!(okSxmD!k~B%d)$%rc}8of zDk654|E2?+wed`49(Bse>mZB{XtBg-rS3@}nF2R4p=^E$B4}d5RnqmLEX8e98V?+8 zCq9$+<7*o@*GbUZW0&F%NCM`uzeup=pO2Kvq4!~tDpbBttI$U)N^!0T^Vfbc23#;| z58!<}Ir-z}MX*<+F0&G)GK!t+9vwy-eyZy4g#@t2Yy__B7g91ld1*s5=FUHIsiQpG z;conP4@_@qElbLt+Al?)wodp)XMHjgV$sf{Gd$xW>H@`uXHJIy#w*~=+1%MrCko;( zDxw~VqoDwZj*>L4ZcSev+co4%(L5# zB&yVt<+4|!4CdABVNi=Oz{~`ut!Lf!5(Jy@4HLP%r@w|KT=7v4e|!pbh3WbXSDjnQ z4aN{lj3_;8OL}orB5N6X_4|R6?nJe5L7sPN+Zt-^+XuN|>aZS}%Avs?fGojZi}{8N zI+5sdFqG6k%ftt4bwR+8rUv5|4cM?$H%ZSra?|3-Cy`%};c*I=qUAv!G?99PlDDq~ z21NtL1^k~jReiQ{Hl=(Li(g{5gi=&%BsIQO&#yIh*a#POVpNw^Q?R zsOdRq=n&Y#`vS^sec4StwUN6EQVkUN=XA{#tV|}hV_o^k!^C{5gGmU>gXP=&>GZ!% z6%;^{+80E>H2P=L&mxOd8iWk^|mKAS5Q+{9Yd;LcnT)nJ&}C%^t%1GA{(nr3M%wE`fYcrA71t$ewTmJn0Eu%|w|g_hj+ z##bAy?^Zv$(C>Yq4Q&yD@Iek;#J1ah_8~(!h~h2y6(yO^j0A%_=+q@eMMBN+`$Qm7 z^8RGx9^okHv>Cu$_56_A3D3Oo0B7HS1Qq0T4w8n@}5^PkR<+zRAkaaTzOTz#U zMEY)ti(q!GNjR%x7~vJK<92RRSw)dw@#&L;TP{6RL8AdG;GHkI!q*j_y)oRfKj72( z4l>{ARyg&!^9MLfWdFLX+SHR%W)h_9Lv!SRydc;Zgin}P$Btyc#Qf{$pH~NlzpCev zYgDp6@9{b&L02hVSSCcnZT(5G&#)?+e%or;e{|>q^yb{r$~lFizhxr5tnT)qF0Rrb28&QUZ$2}g%9{I*30(tz?P76>9KPwy1|_k#Of)ut9X>2y;NO%jyFsGvpxcx&cm3&dSt`V!+u(xMz7#-jp4yGqP>b ztVrl}h#JP1%l`v4%wJUB&cb|$I=hkxD;%rYFm&P|ddvhPFY5Wyw0M$)?k)!3R~i?& zI*Jr>so1W3m<4tbz2&~rV|cKVf1gwqf~q+$%9=9WqUPbK=xvg#dffN#ao)q5Vq6`W z@K?8Sn32;EFi7kVA))Rvev7p&{XqoXH_@C)(iF%Y>TAGn!wC zVp%xSx^BKNx}FZ41>>iazWoy8PFFB{7OelD+l>8lbN0gVmagFm4gV(|JuRSg`!g#P zg5h{Z2+Y{sX^uB}4nLo44j*4B=@*;w&66G7q*Gk||71IPm7mx!duEWa5QYc)C~Puf z^UWU&Zoa)BcOe{ma(muL$YnRoTE{e8bBYZCjw@P%xw-_7hLtKwmi9nLycY&%M&E<8 z`3r4jU*%%F_9xu!e<`Ems5i>XuUcH%Mw#1bl!+tpWtPM2KEt@IR~uJc6di~;OqAl@ zxcg)#&&3of54A5|cV;&UzHtrIWCxDq$+;tWsZ!jf)~9Opst#}tKTi_e3pnc@D(!J? zbfmvjNrZm;4x-*U;96s_@$TBgF^@I0liEnw$ThtBWUzN|sMW?_l*Q+ahoJzFd zbojL0JWgF+xpzC&Mr5uMbxdQqYmJ1Q-(b*V)WeyJ)9>T?d~D6Ub0h3TOLxrGmv0P3 z{UAyxqE)3|nyXU3n$w;c=OXb|MN#*)zpF*s7>bkKoq4&gFY0yNBjq?YL5?iK?O-vN z)fx&3mreOyXzQ0T*B1)bMqaJ&Yw+eTI=}MgMB`UnPRLCe*t(^5shQzTZ60%)X`zM4w#tHO~*%g-hL2 z0|X5Riu*j(T`}$RJ1?y12ex+BlxK@-qoUJ$T;PRq?vvjJjS5V$p&=u#P2^p72GqoR zzYw#qwR*0DBa~EfmKI)_ykM=@EMVz3L>;&#PQiF?l!JSV=qlPhdM+h4{*UFfQV-iZ z?k_oZa^y3&l4Qd4bJSI~Gt`{DqIKRUYr}JsYLQ5pcr$!Pn!NZ`ml?XZ_?nYyOo)pz z&pQ;j6IX!HD7A@8={R11)4zkjFpbH^j>rL~EsKM4ict8naeDgJYGtm$o$4ag=zN)L9HKTgg`qkdtTu}wM-vkODGA6a z_6vFhQoN_h3GzdGQ-Yl~ja-qDo~SAjYhY$z-Kb|ls9G)QM@qjyR5v|;95%EW~gCW~McI=xti`IQ> z8cX5}WfZD?af+EAxFN5Uq)0{^j9sd&-QvXM1Y;d^1j7U6ka(VHrr7jVo2fxU9n^1R zb47ZlGSqVwUga@<)6p2v(BJaBBK0PcPl+Mr5??xBGBiy?^(#!jIin_JpoWkm@Uml%u`L58~w2O{&=asH#Ih3r%r@o#Gn(`m&d(G~?LO_PoB_6td%$YHrP8gFcjT{~A zrFj!rAnw-{L{6mluD^huxldY*+KV!nMHbMgWJI~{adqL(nFP;djsbJ`&d|lWo|smJ zZr1%P#wBd=&6G+Ux~n=>Z^MjQFY_s5B)iPcN_SVxM4*%649!L|l(bt!&<;Wf$xSFX zJJj(i#RJT?^(mfyLbah1mA0)IXXL}#mNQhe3PpRhFkD&~WV7zEE9}40`EcS3UN5e) zdl0t-m!~Gy%IbiPb=B&OI#thN$OwCWlVKL^^TwN=(fuHhz4lrXm=0L{0nn=&Zw;K_ zs6EWNr3VM}kupqDS@}U?y5%L_SC_LD{!?rR%7WWSD7&&$#iI$A=Px}`WZ z5)m=6>5V`dP0VG~_Nnflj+u_cVS*7!9!39 zs5!Sa%yNwrYA@G4mbc_k#*FCc5D$|YN-1-jqU#%c{Wzqos_psS(PD_YGXG#I3mff zR{-p&Q@W#p{BtN$%7exzNqraa(C|<{EM>YHrvQRb z3@TRypuLL{W&X9w^8!!wXmRpsC<8~FXvM3}F+&nQTsRZ93uJ=0%Ogg$%TT7q-($K$ zx{zP0bp&)RBE1G6Ez`^Qt$<^2f|4<%PDBWgHm*mUs=-EE7`D}lnhMXj->rU7P68U= zdX0uk(7lvBsR(wegB=)9fBj>Cv8lgJ(V|d64}*h9UJp|uSlIe{_g%Q4rC$gxvu86R zKLe>>pq@vwYbvZJuT9jJUPYn(28nKj31M2&>#>Q5C`ir}PYi11E+Y7LL@1Z)5%{?6 z7$G~lzIeUWWHi-_k=MLx=fTxVY}C$J#+Am2l@y-o9U7sb#Y^)HuTxE~7TS!5(_kz| z3+vWV-llY(4_Idn%_#`YI;!$9B=^wDOY&z$0BiY24Y+Edm^P!jQ-?&A3%0*Z%%`CevD zn4r8SEr5|Y0BIJ74{5vis{RV}c3)bE9c?zh@1vMs{JG~7vk@F}QN*g4<}p*GX+*ib zR;?XDP8l;tFS`e0pe~_Y*T9I&#i5JnA52*5nN--dCTA7RN)5;?c~>`1Y-$#Z*dU1j=ZQ7rZ<39ppf|9D8&dHYyxSU>j*MaeoO` zo6o~VwUSVkV8-tRD3rC2#YTS~qRISb+yN`pe{QJ$Hro8|7U{-RyF5=Uko9XH?Km2q z8ry)Ru@0`WfH?u=to%0y-cO|9++(r-2jt&0)9)Y8zg_45A_8=eYUI|q-e>pJ!30w2 z97mSZxpe-G$N4Ad^y$<>?9G4REq|i!9;O#MF53SZ^iH>Gr`74|r*$fpFVElDeYO1v z>EEdI&tX#Wtwfm-m*c_goqQ=YRF?aT0_0wk#t3m*D*u9R4Wk?|E5+#%8$aP^Na`a$eN|`diRIE$x z0i9DJz4+{%D~4PAsxsGXjGN@2l(>dGY{J+-kaw?9dk0|2;JM!ShYD{qp^HO6W`WwLN9bpH-l`gc6S8IJ$Ju0t&&2mwern-m;x5 zwvx-?$mPDo-De|q*(P_CsvDGfppL0eYH%ZB;Hb@HMonh^3EB;mj8jz4IctY6@0wrFND-(;F+(yRo%rF-(Hatwkv^0BBLl`&< zcp++VTxpS0_kXUj6fg(GeorOoCB%V`Ndmi{Ns6ecwH8&=Ln>=zLS0X!H)!Q5KC;UR znK4hcGU^oEYt^hp+xz76BJsX5ER0so-(sHdbrDP}Zm}U-b?Tc?dI5X0xP66DPiJDQ zcPNw>?Kk}0@wK;m5PKaR>s!Sml|;*N=>*dO;xM5Mh+= z5~=97jh`wz((2#>XGjpy?UkZ3*x%R3 z77-N2Q1TsAN@gqqaYKf-jIw*CWY^bNx{Y4B+wnxFuyN+H+}bvDH-p__CwL_MY@*4EG#VO;SrQQ9;M1verf1!e!6Af*Bt8Y~R zHV1@(`BOtszmI=!&F5f0YB>#cxy}{)0T;Z;zv^i)ogRf0cJ0!qPuD5mHWIJ|kvd#H zcX2Ef@nI|KVUMwKdKy&$xcE^zTWMEZcTP&4RbGR_ljO=OByUXC%U#FUj6Lb2A1{@o zd;Nd)`wYEGd)K!PmL)HT`eav7`2pj{Svhy=9Rxizy4K%X_{A6o^Ic!58f1^+%@MZ) z#`h!10CKVA&9@V|p3dPtJ$vkIq+243uCJ~^sil;M1FAaGGsZFD^nQu&HL0v3(Y=W_ zhj;j9%BR9%o4~g(aYzAh2Vf(ltaVE+F9?0k+;7YLgLrI1`oI<1E7w{d6^cXG3ap-< zf6|41$A^a4ef$ljCWd&kl_l<(*LV!*p%^+~t(-t?$vwDk9Cly}FsdA*mOfk_`f2gk zq_k8*Q0GHl+67PLo@eG|{g^PxIiV2TjP}MTe7==4Q+%2HFagg4U>~He1xUct^(F*X z5cQ+XqnYs%z`ouSfO4Qe#Rfw)cx+?+sKdgZ@XGB4!28i$63yfE{`-VLnu=V7?6pSvD6B)eMv83oeNV1d>5-)zU zfhvuhICvpw)^y(AbY5lq=wh+V-$ADQhYg}3anWB^c2Z-3dVd4<(X(m4`j4i^qB?bO z$gTtIKM(2c(SfUKfpy}_EnuDKu(MD1Cw43*CT4(RZ`J2umw!a$q!$*s0P|A*Nq5Q? znQTvy9^U3qJayJL4i#+{L#r+PKdOG{sE9NLb{h2dXh0{Fv9tlX1@MEHLYLT|Rgh?r zejMzQ*|pQyUX$6~gKdSn$$P4WQMJ&Cn8-fi?`sF%DlxDjU^rE`?Uyp`?NIL@@b5{< zT%LTxtX9`_BJj~8--oDG2Gi( zhN8^zJLpwy@Fvzzt)hF$7p`-E)6X|VXrXe^!15NBoJPsd-&WWv)eiJXBN`s}7ehGlJ z0;eY`FIo~c$D_1gd(Tx9B&6z17_3eg8C_N%AAmE9o3Hp%2z!N}Z@KtXU>K>>uhme6 zBtCVTJ(93sX(HZV$dbrqqpwjyERb?g9i{)7l2%})?91j=tezhJ>|4M{%szm_o&uwp zS9@PyelnsRLwtidDP7~QMTpYk`?w+ZO-J#_0rTOYI_$u%Zl`O2y%)?L#P8QorDsY~ z))h}6=b6!FF+$V$t^MrBVU7!sZT>?K&~L-wZ*PIFv9;l`U%8i}l5VU6tXv;Z=+~<5 zIMPa;L{mTvb9U4{v}}Vb%$3^`=>MSgL9233%qh>uZE1h~z z${Xnb%O^Kr`J~rr|L8CW&&jn;8v9TK{ErQ$69wt%a#)>ifJ11&$=y$N>Y;%6Z&v$v zqX|W04tO+S-$6P!42o}?%8YIT%UARLwt2aeJO88gK2EoL>g7bs&b%MZ_n@WwfMu39 z3E6#F#EbrbEYwl&V^Abxd&G@z9^)#k8w^gw{4X9|H$ts{S}~60i4~Br$6u27d4Z|} zIuCDp*R7<|VNn-wr*}jUPXCzP+J}XkgEn~VInmCGlQ-(AwtfWY@VdW)LeVk+4{XrB z^h2mnjmoDy_HGrnR##t1|G|O5CMct}9J=te+7)Bn3F|wmm;GAK>)Ke%y&&v%kf&VA zONF6Er<9Ik_sSR`V(*H?-V|?hZX~&@?tE6~Fc9~w=Wmk%TCi2l!pACY4pPZ;>;zX@ z5=~N7Dx~0#D~Y;0YVou-3k2hsJyY+Vmq8)wZgpA{FX0^lGw{u~OJtWpTs-|FjlA3W z+HL6yTUdJYHviV#fRVq;XS3%!ND%1i@TM#s>_w*Ks6|Y}fYU+b9~)VJfkm)5#=!h} z=4HnC(&v@(fn)f?DQdav-XU#?p#zv@9L_IC4yUiD{s!_1F8FhFj@eN=v(1sB+X$j$ ziDe$NviqeahcE}3)vM+Sa5n~ibdu)j-ptf|+dL`t+GlZfHEO{Js2BjV1m;3pu#Y}- zt9uJ*q5U=(Q*xzS&Jap*ZJlI}Dy6DNAIc|`E0Gi9zR zvqo@8`Rn&8XoLs(1$t^le9=TDqxB5;M@uk{`Z4i_VQ$G5!49W^d;BBP>MK31%cb8z z>{t#Nb4^Q#a!OnEH_-+vmSvGcONo>cyU#rLW4NJ|cl_>eS2@Pst8cOE(OYJbEa^TV z-tv*`_!_;Nu5EN7Dbh-(gp`ONAuTE;;-ZmOLQ1-G0SYJ`0ty1sxj=H!E!`sJU`qLZrShqJp0+t_nkAw`NOdoxVUrP^Pcmb*SzM{^{gz8-B?eSrjn>YS9Wp4 zke>HS_fFau1>TU4+zJ4r`<>6#0<22R`HY+Ag*MupplZsuuqU~(&_HIBeYb@H6T4^a){FB11X)S6+ z5*ifB&D03w8k$h194sHh$ySys5+3ic%7omBFdD;B1t$(G@2=Q~)KmG|Z++7l=taen zUSt^tIh&q}hu>KVG+uyceuC}ge{J-YS11!g3jZ3w)Wc7cF&~gJ-(_Gvq-N-_^v>@2 z!=QK6umZbn{tT?@9YtDZZiG9u{mK+UDXgE;{hm~dzS*O-aaUh>aWiVjWO#O|z|z;U zuQ=*#M`uO-Q6CK4YXQGk;Aac_vRlFa*QbIJNPj?oA)=ox!M>P=3|hko5m1($2e<<#aDoW&{_t@`Z}oOVRFEoIi#X8V80>^ZIE4R zSgHy&Q4=q1Xb!CMYAbB#d#tJnKy?#S1QrjC4_%UN$Nj7pC{1C-`TG>f$?aQ?CFb;R z(>~oxr}<(cs)&S#unF>29I@Z79#T%|b&|^z7#b5!9j<;_!iF|kh+CVcK}SwK{2)_O z=Y}npg)>Uz+!}zLWBipLA;xfUDUtAXT&GnOSKGW|9=9=F>*Spes6%cA^o_HFWvIWh zBz#LQ+et)MHMEVeyy0Kl_&od8&2=x-7BQkdkdb#mzAU8AcP1TxnnZA1UA4;%SAwmQ z$IWe%Jill&bJ4HXqIMS)Z>-2t^k9*Rr|wpS<-*=bsm!RRot0QJpX+^Y7vtdv^$5{8 zk#-FYN?Lp-pPua`S1pZ__R;1?EX((q@V`@04UU|7tj1e?;iU)}Hwf!-NJNSK>7BfSlh~O*8ItZFEhD0j?^s9J}RDNDFyM4dgF)J$b~bcNxh<% zzb%x7ThzT38{Vnn8AO?$jpo{Du7J(;^}C3Cf<=)uP7Pmcq-kjD)2OxLoRL+rb7`&} zWu)LCNZl$5o^Q}7mkDoF&_-F}uWWviXR;sNJ7Py}+Fi{nJ71^mK&3uo`A%qH4Bp2U zZn0W zG2dmnoZ6O}HZn>!GVQMjnM{Md5`epPDr%a?buN8oS}V(IKQ@pUWxl&)q19g&umY3+2=XG z7=OPDRSY;3YJJf?ZA68M3y)J8{{fyY12OGV8yW)qa>>d=UKIcJ9jI23q`qX`DBZie zg))KK>mN}dUPO!DFT=JLcg7CN*Jv{~;CY&}+vK6}5{JR5!AGy3n%df$UQkox?Cpg4 z;Ws0rE0E=q`?`DuX}^E3KrZ zqgJdjjU4}W`DpMp3F8;n)S5~x5Jp~$-tw#5j$@NboT=adI6e`lHTz@d!fXX;96eM= zn8iJ@Z248*2j86ZWrRuF*F)M9btEPz@x-#K638!!?LX}6QfJ0gQFoIG5~u$*(57ay z8GomA#lbMpkGZzd-eFh#iz$wzb!pHbcc`0?hm`Gi5S<+GuJKWzAMWq|Dq$)Ffbk#= z2yF2BXfvzUn#kUsWx{w(8m{o%0ukpT@v_?nKHU+9{ECeDg=0E|D9XK0d)~&pLYqFD z_%b7yCN}aiA`W_HXE5X-d7uwUc1U4GYnlombu-{?!rgqp)c*s;c#LK|{zN(sY{}>2 zXB{|-z`wv1SU^Yg1;_vXrJ69uKgyNmVTQCk_;px^6)$8@Qr-_l`}rb$>@JHbwVt0O6>>ug1;7V_(!VRRP=%2-$C#u z<`*6OsuS%v`jY8$8bqBcefJo=dGd?!6hD6}4af;a3Waa+ET~Uux=j8UyOP(A(w$X- zv8t(}m3nW^$0ia*I^AJ>GGyM@LaZdNzAV0lG2Tp`FiVsYVmPUi7a-GlU=TM?ftj<) z`RN;pO}K@dZ+K3IBTZVpE~?CK zp|AAGJc-LP!U6?(@y8CQTgij!DxSSi4ZFOBmbhKZ0IpkPh3kZlFj+CWIvxL}I;mUI zeI^IW6z`##&_&N|ly}o}>E=_<_0ZSElj!N(Y;;h-#RD|FlD3D|umk)old0r5crirM zc3IpXX0j6s`6$mUVVANzia!-1d&?7${T_0vcP~7`wTcs{kXH8`u$IoMA0*@f1 zc`$OT!9}4?w2S#JS9mSSXi3U|(V$6jP0cn}aC7WuqQ$7!D7UBH42zS3)0l;PCknEA)3lR*hlOr=@ zU_tX!Vx}U}ZId*`hf0dZs}t@5R7>F%nexj_2Ah!SLx`#~pBj01Eo*_Dl2p%lw@~+{ zz+H8&ww7U#ACm;N?$pp)CiOE~Mw$;8X4t}CjKTZb)g&VO8qkg4q2x%5k8hnCK{Zic zp?q$W`SxO7{SuL1;E~1?epF*S4#q0mi?B;Hte@|WJZ$7)Tt~(L-9Q_}0yjszyP;&d zU@_4hz`dC(!2PhFGdrCtCTqvp_>yXVEw>G~l=X9)Kn#;bvgZK}RQOcBU#4)?;69U@ z_=cmnFKWX^3A!S|VZ$S6aw?W)(mH`xhLBiY%`Uk`PeQeKq&~rXWwB9rs`o!pkQaH* zsNdy_tV$8n`mD*UTYo(v=8b4etuIW=paR{A**e{cu|pOpu(M%9$n{hw=cP)5i!$vM zEUNW8G&EPYEvryMN*)b;2i1HBg$t)!3rGt08t1p-)hK+tZp`{b@)Zn&t6#c?Cc7%V z7bQl|H2Lw+z~kybm7%(95j+egYG06mE53j{)V4XBX{!eymbrIcE(Q9Zi6|D*{*2yp z<>24g2!S8Mc4TIg6JoX(E2@v;O6%QS;Qg(!ak1#=V8|FCbLdC-tah-3(DD$(gTlc9 zxXxXM4wzIJ5NXsgEs-vvi?`4Xi&ozr9Ia4k+Q)A{SP6meW=5NC1_FLB5T}R;%ZVat z9eLLj5mLecA0469$s`z#Co9{j zN`h-YH%EWCZ@LnHJlP{a*zwW?2nYaUtuTh;18^|HleIZsLis#t6wzXm88m_`L2o4s z{a%{qm$~GJZZXH~j?*8mtMi}9o6_ZFi!8Yn%1Liy0nsV~-iz@dV;s)jvv50LX`T9& zMZ|jJz}c@G508JFu$~_ zGOA0kdVzLa?7k^DQQ~+Rd`{lW&p%ifSdWG6%JBq#aQXjF$5ke;dF$ovkR{j(cgogT zRMN48{>?-FcgFII>VTTE``EeRnZ?I(15e$@2ktCL>v&myS-HO>94Ujq>o%Fhw(1UP zkE24w$qOv#yaWHg$ke|x7$!dZH>$O0As*zoWrCs4&XlOc{`WWXm&H3*?_S80W3Vp_ z34>2UtQ_T|PtMC}`WaAO>3@=yf1!&L3B)ITSfPLKH9(4h=n!Cd787`^B~KO?xDx(z zY2j~@Hm4s2PV&)v?Qs`pNE7~@3+nvQpc<^R4$=chCHWxNRBn;Sf(>+m8J$Y z1m(>P!D|T<9fdb(USBU=4n!eLMfF-4x=}&sZbhyFS-gacyaDTjktSerV7>bIfR@;t zI56O?(4FGYWXn;y?jJj|PL~T$`y=wB}87@cjrMJ3y zZrIHxIkm*`G6&3sw~)y&ho6|Mm$BhGM^o-jQy#7{9FO$K5~Y8A^_9xd1TAr7%p0KK z%B4mhxIO*h8guL_>wyE{di0wf2Bp_IsYGzZTxRjCNc;{m5IzhZqJg(MjzDLzY~t9+ z^xiAs>pFzN3!cxU<(uveZ)jd)-JOcrZiqi_tiJSKay^0DH&Cb3u|5%tp*{>J#7@08Ydedp1b>k%u5+wl%08;C&!6A(uH=Zxa@P4^4ac z;qWby!T{i8AcjFoZ@MVVx~U3bWo2@*BFzzU=j?8?VAY57tfL2)p$Q}U zN`$Ws1*c12*~DD8Qg|}SbQ3|%tugj%=^~{1J1EGI&}QQRe1A;~kbomMz?=W)K#NpO zcWGCFnnPD0U%h7z#6HXsYF@nBbHghAVphK-?2;v2F~QV*dD}O{diJg4>H7t;LZC`WKD6{=K%v%#rSn%2MCC z`yMC0E91mpkw!Q^=kcfg9VHAM5Ap0VL()7Mpy1D`{BRCW*C^n0GDp7TaoZ_4BdAQE8sBeH0 z62=s98D}})K>zQMocvN;QoGl$FEX(|2zU^3DjI58wazLFB0J8u*2xV|OLRR)c|3(I zbc1Nd8fd1+qQwm3GwrbNb(iuuS|<$Bg~5zX)@2>6QP>kliFR~DvDJx%BGn!9^ZG?FuDM-Hs^=Bl|lS28&pTLR|bj` zEqp5_eVW(|IrO-`Cjgx;@_;_)s=s2|-R5^wfwrC)5fzatkZGEboHAzIdN)q4Vtgk7 zz|2_*{Vv2aihH_Vo`Te02?k992gySc3fi3>Oe%&q52x&3ygrGwKQmaHj@&k>2JtzN zi_=JL; zf7{f!LAy+7Tp`Symi z=#A!oJ%<0@`S3q`CX_QTOcfJY zw9M8RT^XjNX|9yZCdAX?4fQ+*MtY-Te5~v60{o5QMM(QWfaq_#m_--!AitlVW|9(j zEWtQ)NL|y}N80J)bB|p1T56Y+4|POtRo4gFq#Qim?DjlgB#kzKf@cPK-eS4txI?1o z(khZCKTa=|IKWBax>={H^K=n`djNU`moSoz9<#nlsF(?S`lVkAWd6fj@AtuH5sg2g zu5VI*6aQJ9>CSGzVIAD)4(dPL&&h!Y#8<_R8jTGP`yJ#Z#7;iu@bXh$dA#*%W{v=NI3bwmt9#us+z1?9a400Oq_dwEy~GFslQ&=w86>05H6hw_rQ! z!76ZPA6;}R3|HBu-o}rw2+Gtq0eeveT-@467dNi+e!%v3(3Xsb)Jl3~G=858`M~fz z1!dP%dDIfOhL60e>!_3K%{Q3io+w0Qz8JEF6uDb=LbJYreGSY7(oS?_4Qp_keg|Dd zeGR9uGaSzldp+^7N3Wn~V>cN{{>gjlEpb)(q7zLhQ;zX-X54v%bPDqFm68BFpwQo= z)NysmeRgfG7qCqqHZTW|aUQLj+W&P%zwnz4h z8>ZNUJ0+T{{DSPT7xIx02?m`gv@WnT7D&(rQ`?K_1x8I`u4VC~7iEg}n=XJCQsUFV z6#y&3l!NK%atE9)o+TwZCo@!YywAfv1-#``@IoiVWMt-zR%GMSqv6@n;wF;Zk6|f* zIrMXx#7V%P5BPm7}2|fZyiOi6e73p;sK?5 zAAsZen3ZL4e%Mt4D}`1npb_bXJ6oU`TD@1L=D-%O(bv8Bnv4N8umx-8)63VFd0MK) zdy*6as2J6Z)hN&e!oz^KoI!9S0G(q2Kwx<>!2kvfq+_Ql>`bOPtf77glk3sj7a6Gx zXi|I#vE9;RR=$o z0`ZsVETCDXALQBvTkHqw@sv1rHrtY_ZqNJCL2v3y88NU&8rs1!%KaLYz4=!DL$xIN zZ}u!2asx ziq!z8b}$@POps0y-M^Zqe!ZX^(h_gjNZC@N#!9wC#@bxfR_AwA0#hm}N{2{Qr8fK8 zWQZ$Uuw#r$?lkOR6Y^sjD`wN-z>L)gD@TqPj*sUcbNr3K*Gyc%8~w}gAnS^^@y({Y z00H;Ws^5&apWE->PkxUrvv`M?B-+c$+A{5H_?)>r5loKZ@Nmk+!Y|Vyy8HPmJ5N zICfQuJ9Z+Lj?U?7nD=B$U#T`T1T^_+5!ae0Nhs{T=6OaN4Zg#5vVEC9^nG&b6!bx8KiD*cLoPbJFj*p_e5vg-* zeH8tOhFN*?;D5LKe`{*L)QPnFJXCwX3;T`P1t4Prd`1QRBfE71WjUkVL_1-FIQCu6 zeY2MU6#5Z^^U02#`PLEk{=X+J=e2!hd1FXjPdNNh#XUM`3x&Kl78cq(w)QYZT~TuY z`a*=~`$0kIgnVHq`9s-P-<(PsC+2b_4ysH2iNiQ)^++XJt!-AbVXs~%P%?Z$-q@3m zK2^il32$=7V7$w;+FwLpw1PgUrZK+s*6Fjx1tm5Kis|7B1{#b6+^Ra^RO}Z^H#RRp z!L_`lFqQXV0oq7mxcLz(_jj&qk&e84nL^hbQ+yND=8$!|Q52`$2xXOW-JUz3%a|u5 zkTVjba-Yr{S$m9@f${i#T|-X$e%(5{C;g>qcZu6`eq)$+E<^WLpwXTgCRFa&*Te!X z*=S~;QDjKaFQ}Xu`sod#GM70%u*J@gAKM6&8y?W_(#T&^PW2{M=Hh;H@pE!WaJol3 zalNo==(+pJHPq`_dzut-(kwoy=$O9nED$w&&ziE8a6}}2?%a+Lx5rB3^oai;H~)F{ z$sCKz4-Wa0i|_QzUr?DnP>6+8OO0RhqgfPsw*OF{e}@Twf#paD+>;F8lE3vD%V|QJFgPpdxWPWlMMLNf`q{e4YM$Oe4(_ABBoS zpKl-CjHgA6P5rg+UmwoZ$-Uf;=!$IRa=KL~^S|;K>jKzINyg4XfS^Y0@wbe82GfmC zg?YD}Lus5RPz1H!;ZPtn1(sU6sm#`>B&DW222Vt(v~3MmKea@L0yiwpL&1f{a^*|L z^B;^sYnE>QC%W}?nLLtb*E=;=G^4+4!YeXoiBs*Um#llgUAK-k`Y^wi2MbH;cdD$| z>%3A@dFODOCQ$hm4Gnn)8|}>m83ysryJ`NeSfHen^*J>*@)S|ikJyXPJ;0|fQN9Yp zkkeRspwp;NdlL|}I^E!Z=9K^6)iR({YD6gia~zkwd+V&a@RhC9(>*@oVm^0`{tYFE zTqXn;)H}+Cja1lc>7F#rd&EB{IB)5(X=+$DEp)Jbbg%vpeIR@hy>?{t*@FS|aO%iB z1OdTWz^VBT`W9CR#@-S7#7p2A!4lxjk=NasFhtckw#bzEt==foJ<@a1UE2ShTvv%* zyeYfsnD|ZjAA80K7^kH_j8kTW#^x`oj#AwB0%~9ZNn`qHjRqETvtEkzZ*v)`8pvE* zJ>?27pv5}T*gM;sr|kI1!%BNv-}Di*8$2@}hxx0S@{x!=(^9aIoJ0p920OH$HccT1 zSg!UzELYk$A>gd}OOlVRIm z*@tp5WmjnHh#fqJxNNbcY`Xt-?8=ze;+N(T`e0pqpC>4{XsGV6xuVRl^k}0Lyvwrl zUmstJN>!PY9$8Q_s0=D^MNg%FzC;wRz=Zf5A#w)MU%ao+8q@Sq&-UQg_Vl>`9P}_# zJT(ypdb-j9Ty$LDCsR0fRIA`|xQBhLouaDbv?t%Yr5iWO+YwFthb3ulzkY;rcd5<{ z#Zned2-GoO`wqIgc^_aX++=Rmu2jK_Pi?wZUYzT%oPd7iR#0Ol2z`$E{f>Fk$X7UG zht;Ae>$SDPuG@(LX2uz)G*p2QM0IjCm<W>+| zYOKT%ykj;z`$AQN+E}x?_+iB0LU?ghS*%h~eyKMo3+s*r^on?u>y#diFK*j?_|27h zNcF{30}YaHmI;Fkc{T+Nf$3bE^qs<$%hEe;)~5IdvicRDZHsmDJLQXtN+T{1kok=x z$@1I~L!`ozp!X$`;=_#}dpauAw+ZDUh2M3git}cYK=-Sd92Xif zond-#{1c;F3)nuzD4MQ^qm1`(V~B-Qdtam$H@=lNv&5bCZHC+3KyZ@;t;J2tN0Z7u zc|K8^qNT8 zOu-gtDa2#5yTzT;!F=MFE9bivHK8=&j zaQvDZ=!nX`7b!VhZ>erKIVKY`Fo%CH_d zHNq!=EIHsB`cvh>hzlk4pV{jss;mxTQ&#A zYm6%J?eLkb9nHOuj{$mrg^t)hrCJuf)Zx3Zn{z%!mz3*6k+m!uXSvC4T_g+^N52il zCbDzi7VpsV?jjvZ#nhNhhdi4-D zTjQXZsj!!vUL)*4s6Bu0U`dK^uC?NTyFu@Oj(ckiWuw)6Y>{MSDeC!EKG3?!jDq~` zo%$PJ0#P6K0cAwMauF;_VypOFBMZDzo!*jeL;>xuEA0{nKOltiJwlb|Z2DS`+H1-& zhNNG9b>EN5-ai-_8^)meL7%l~Wn)6}%vyZuM9! z4ReSs?x8KT8uIeI^UR_avCJ%aXWc(3KdLMNnrk{ebWld3lfIVN3jwe6_unfA&I7x9 zZr1hL@~(IwL#U)X;VL-BF#DE2uhxC*sy#)&)1(cMnF~6sZ}ZZjZkCg3h7?6qYFXO- zCpij1_|kq;sxM;RZcyjytooP5jt%dKZV~5GUIri~tMq01Z1}?%;AP#<7gBxV-F&~` z2%7L#T%6l3ma~gcja0feD;F_8g4>aoA_JSMs?$(V zRJ%GwG3nNDd2Hv|cTgR#eYXyljwV1ESp0khpYhac#RJ^ydrkkPHS7SH=*ZhpPr;@$vq?^@{C~7HctW( z%slqRTHjp^EDN3uruBvl>qDE#8UmH_z&$Ho&gv2M5z@D#eD7#P??1lFrj;0JenTcm6qX2uX=@zJ7n2AXjyA2C{v5H(&Ed z{ZzEL{q;Y<>)z|&3sn=Qcssn9x}^qb_aJ;yIhPHd^?k|6OheyAXc`wa`?6VuKWFX? zS*foO|gkmu_NLLMPySZ287#_5Bf|uDhLL4 z!bKz{C`el>*J~`&ql(-Ju04G6CFP036t1TM3E?Xz^6^D<0lE+cu0@UrXz&%k%;;w~ z*6uF*vX^k-zY~_V5WbM}?SPUNPSwlV%3K3k@l6<1`1Gbg_k&W!y9V8$K0^Jvv9~BJ zh2cCP(1UarW!2pM#qQbsiWal{eq$lQ3YI1x+X-gPmt62$;dRA6nm0lT68g3k`!scf zTAsGtzmC5DI4T~CG^ou;Zvyr80XTV%M4&DxDKX(ztB$GZ4O&?(ymk@}w)#fab+RyY z&{v5$z45V{R#efIdM}Fo>cQ(Ihl%egtR~tnvp-7V=6;LNY|`F>U2dc_@hH?WV@KDR zx#D0iy@yI8E$- z`o#ZpQ62@gtQU|e?bWeEPXLS3Z5;*DKjn!y!%(4RPB0)?Xi|VtqDa9&YxU%IX&|4QfL!g@K^vc*NH@`L~tsHAP^seVf z{|8!Tub|#@JJGh2tHp$7#v}KB$n612+ZfqiXrB4toc1ODi-~Mz0$!SZ6(WAwL$q@_ zJAj-HfYn$RU^SKjSd9U?09gLhXFo}t{={m$uh?(RP$~tm8qXEj%I3$G7~C)=K?Vs9 zV`dA21$#efDu13yE1UfAJLOd7Et%E)zHlQbu+d@^$w@W%m7dIHEA3={`41YTZ`Pn7 zU718AZdET`#e&8s*lJL9J^#@C(1$FXK&joVW9c?VLq~FBFalNZt@qlrx=k$+`g6Ig z+n0aPMCtxyltPv3%ePx*)$Yo>YH9i`HX&kWYG}7MbMd2}FD0V8giMIohK+S(lWF>bIz#9kM%KHw&-K6Lh5FJ}x z0Tyfh3g~#XPAIPwuO(&<`#sHr;#IL`#rZvpN`7C3#4s>;$;0CDm+`yN2(|+wY+43X^Xx#P#EkBVgXvj$ll&%pv`)%a1uTY_I1SvXXr$L`splgy_53(EcI0 zX$gojZqONqs6(=P7XG}5OwmDHsZ8S5j(p6DM4wqqMxATQZAIT68r==)KwJOQiZeZ6 zxG?%k7v2G^%8P*ad(Ow#0xw49kVt8S|Hh=x=&--D zuE<7~tOFC(S4lXYw%yp;$i(E$=UW;gW-%jv1vJF$ zY#&qTeRcFeEH=F4=TSsuYCc>QOg_v`dc5=EUVs$5N6bjnd%ay(SG|rWT0#mJ2! zB)ueyyetyn{c+`BZ!y`6qptg^qpSS<(hcILK|!y;xr@U#cnD^^I^I%=g)BCFBjY{_JYFJ1&V2Vtu*GC(32 zkUaadxKp0zne{mp4mCJ*f;l^r%V+$MlM_>vD9kaeu#jtOg+@=`-~!K$&$p$MT_tFI zGlM2P!j`^+!2K-YlD*fj65gtxy{M&skxG*6t!(33phcQ6neUTOmk*{(C!~>Q#K63B zCs(bwTc{v!86#tlSeV0c*{Nh8w$3(zLRmwM2}&Kz7ML(mu(^yos6R>+t{TlVi2q80 z>;n6>(6>(6FKPH1skF%*Iz~c2v#19_&r=4qt&@=hR_!QKiC?$YHSEHK*r*H3^tE4X zQJv5a zmsC1WDd`OlvnKGWH?*aQVwWPtt^Fa znR4%I7(88fYAsi6h+BuV%<|*{o7o!(3vRh zclf}6MacE%bgy&CpneG2Ur=qmIzy)5^mGDhDXeY;UMUEt^C9M{G*T;AJ~6u0IdKfD;<8A;y{5tftbE#slW4^U!Huc{AsJaq5DU2-hB@#=Wm^-m2s% z5@ilJrZoVc?Fp@xQgqd13|VzUy@CTOX{hAA# zyd!`weICUbQ?i$}k0y>juS-^PI9GTFU9D;zsS$>&n0BkaboZkci4k?&(;=QRf5~== z>!_YPjUNW&|JVNq0KBo9uVVYdTuFhVuL$i2H5m)(zh&yP^FM`t#VIWo@-|8A%9!=g zQEfCCY-23fe%qyMZ9EAIZD=r=yghEUOa{DeU;U!a3Vt&%G&Tx0NAWF=XL%#>%>TaJ zBXRY#%cdX{7Zi30ikjbYZm+NcRPeIQyB8+| zTg@tUqF#Csfta?WQ3Z^$QhrM^jkLuD0 z*OfYfz^NwyQ=5SP;7;bSRB*^YG_`DZa*nM^*rBOa>Q)~f9a9s-iC9|`M%K^IW(4${WaBdfE+t z?*?RlyrMcscIjE7u|u@&BsM+(U$xj#BzLG07Y|jEttcuMPq_ay<_1pvJ0feBSJ*aR zY#pdSUkiE`!FOlR;(n^h6SJ?<7?=rWqNpmlGg-h3!WbqH4Nm|19kg8o@xW4f431O3 z2%y4#b``+sObOpHT}j2IJ8?TtzJ0T)+<{DJM5ywJ*wlP=WN=5dPGu3OY+@6}3k%^O z3<(VM65ryGy84B?i3rix-TXaCItTqH4cZ=Sa@yBcpEnHhzGiyumAuw@0uFOv&#Vd~ zMk+w~((m~Dz_`g~e0C6V4caY?*S6k25%C|?C zeYI!Wc%aw>%8khfv=MF3?F+Wp?(fWn4q~rmcDHJZ0XSgN1BTO8O=d3G2i|5+IN=W78C2Zy ztYk8px4eMt=d2a()U6MfIOH|X`8y!0{$Ci)d94!sEL$RQZ{ea_X{X!LR zM1=b<@8o|)K6p%D6^r!2R^UrrRs2JVk4vn=*v+Xr;z!XspxuyqF5D_nYXUK$4gOL#2t`0U=n=8kO1 z&mz9syM$^9QZ6`Pj+U@ri{L=xu+Ms{MQT6s+;8y!eczwxi|!qITEy;qlRnP(q>DAN zm7Ae+VPOI00-G-~UUm5zyVGpz1o{ReIu(M}s*AIZdpOBGPq(nJb%FSe%``q3^BULP zrD>t-vGS9fAoR*-MI9p-lZy}wkQIr_B)mdZ)$0X=?{miLwr(zT$4|90NEf8v=%dB;_-v~e+b(ox_s_B^2^6&6g{Z!XiaHd! z^o|0>Sn7Z@j;lP8Q(F(WSTy6ZdZAn# z<+i%~$~tm{Kz&f@fM%QIK6lNbdTsYj+Byeb2H~FyIQdu@Pjtey%>4BOIn5A8iDc{P z9X_}hY8;T~e7D<))xbDKt=>3OMKu9WKiQ{-J%vK$uApAyexfiEjxbF`YNm=^u6#tl zYEfri5E6-jUB(S-9^AznGGSr?q)S{>Utp%oo?Qe=op-Cw+#u3h-xGnE2glch~22CYq zISuli?!{S-jW-C-{!@i~?lGqR4pQa@uBm;7RKvd@+4y&q{<(deGQ6E`fgkYnceOUg zy27pO5AyQgcj-UAf-VJYY9DR_S@Ry|f;<4;{>92Vyut*?rwD%VE&1I8EZY6%1I)wm zMcHGi{J%t&5_0ON-6NZ({c}Ui^N2K@&@%yO6-C-Kc|QAY}G#+Cj>%FLD2NjnUIf zV4K0G`mvg2G>pSn9N%lNLP?{H9<2uW@Kf*$*kXR|P7dDNsBg@dL##`sw;RrLeg2%g zY1k>L4EwjcnJH^oO}6K}TbmKV6u^rGy!#Fs2G8`mf z^Y2*%!Z9GE2sxq=pBh8ww;VU78D9)d+euwOAAp>lX9;gupAPUeC*^^fQXpKTYku>=)I0Id@s&mK4^^ zLymj1OlmjLMX@m@<0M$hMqzqDmF3Z$^lH*YfAa?N-WN?x8&iT8APX(h+pP%H1VLR7D#jW%|sE`Iq$bWk{SMdIs5}=)Om)>zhj)`|2e!Ud3-R6Vvs+Y z;_Q6&odn#!c*=DE)pvNaT3=EqjIIY41`oA7VLO~a8;GTW<$69 z>YGI?+sXQk&!#Vh%&!^BILW@8;|jBtjUtD!w07V5U|$oc$lbVAxz`UcN4$opa#<>< zz~*-2jh&vxkqg3CGpGFdFe?oGRSmQw$UhGiUz6V9nVg=@7wrG!pzvv!;8T`yVpM_1 zR;%E|fR0ItLZS$A@fXC97iyaR96p;J{NPL@N?p(SnZeOs7O0K^-t8{+fg4SUZ^MPE zZ{FmY>K9p6W8860^L=LVip7g~eLE{0Hs&&{=#+l(8wP{I zbL}rEVorZSslnhv|MKxUhEr1b_~J;}vO9-F#$t5e(6^MJ>gCe7ltGjwZmR{i2t`HH z?oGOH5xgV}aUX z_UP7@vsbc{ee^Tu1?JjUU3O#2l2od;p3{WFyo94pQKLKS=8G|g6h6~Oq!TLwQZ!da z%7jQE?}jrI^#{d!`qVzZb?KlTg!C?syw?`s)8-6W_WP!fU%h(`};T zqmALNuFb^jf3aa%d8HlifJ(yGXBzXe`4bDaDFp!+Zm%zu{tArL1|OLj=g}^HQj2?& zP$C3CDrj!c$Bk+3P|jEnK7tyjW%wD9&!aVIp_`-GE^9c~BpAN=hHN0)VwU znw>&Z)G_xC(i1a{06b}+w=>{fhLr&CjR?JB$)Lu-7@@uf?W91CmIwoImo(=dW6Y4rExn( z*ByN_orWFqSx%7guD7h)aSbA914SSlNi$Awp-|{lQB>u8HACq8l%>y=4UJRsgV$im z3t?-LNyGNha&M>oI^)9#;opJ^G5mbVZlWSlY_`}v(Vp<~ZT+fQK=cjX@`xS45Ntb6 zwyfV;e(Z9KFu6XOih^=vt7cpRD51tMxjocAunzCv8@62-!oQ)Yv<7iE-GHS&;0CBh zR5Vlj)a=%43f3P4YqUJ|1T(4D=Dk~_dH%(-5KT+%@{2Gceln7hjGbhO&Lypa;s|?_ zgn%|2IwPj zY1oC|>*k1JU$3#j9i3Mx%QK&|DOjls4GQ|VUMO*IcQ>1}lt`h4iR%VG6eYi? zmW!~CRXYRA8(WlQ_1WsQ^HjyW>sUqt&KXu7;`|7EJR?V)!x4PB#7WO)`AZ#dFx|3h z|60Z7$IojlYZ6?~qUGQ>M3+xKoo={}&MHDe+~`Y1YqgVvTmn z!KApal>M=XV}UgFpad#B%BSRF9Z;t!5Ssx**fko_tw}~2Q!iei%9Z3@DeXXqBl3vXd%0O!iGM;ZV zM@Hq`A5woTMyick^ku8nKE}gXr$f8X>1op4mZyDL6TKf^Tl90q+3c?HmTKDBG8Z{| zFO*{pS2>PDHRikYXA2@#DuQ{mjhp75X`u;JUCFW=YXaz00fSPJES%Y>Z7gONqQrf` zGf7TUYfQK$w)H0afv5Ov_`_mPLYZOl)ilbsyo^mh!EL#UZs?8pu(cY!L9K@&xMmOB z9dz8@ThGlizuURvuIb@I>F0xNbTc6xpFgC-Uw9i|u9BB4Cf07@Qvt73hD~2HrjM~& zaEs!@T(;@A+gcI=t#X~8Wji(ibrg8n;53K67xPT=2t9nwpa;_I!)#$(!$M`-Cl&KKR@AO#$8bY3anvrw+!W4w=Isa%{IR8 zdX5#ZxQY^ zlu_oU)<&FptM$IiYl#XL$*3487m=`pv#zhWL~shB?A*A7X`Uzb>{9a(6i+mNeKM@) z!DO>;gKQF^8mMv-*@0xxSX<+TY%hl?seuY(uU_XeeNL|xbVvh z5ht7FOoZ;V-|aMM%|9G2H>{-(Z^)0ZcE9zWA4>A^^&{+-X;B28O3qpMm7G<*SAv*d z2Wf3c@RMR7aaBfJGd*+>@~_7gSf}jKFBuEjwp0q$8hz~9u%!3O$7HSAi+=YsmDb4E z9-vMdmaM(1Z(XV zXtg&(D$!X6^z(jJ`g4}Mx`IVHCcq38P!HK63HT0@>a#*HQtCG5^t-YAs7Gr(V<}Gm zS%A5ELk*G+ju?e!M%;D%lo#mnAV;}liiZT`UNZE>IPib zKnqb)8YKUjeCP-7zEK92IDaB)>7aTN4|(aEAVgQt;xQ4{Ns~ZbL*ymk+4;DOQ_`^u zv?)Vy!OMqzaIIsuup}bw^$vRjzvLaUln>|*bsov{Yxh{DWG{|4_&eH9xyz%by{6GM zPA+W2LM1~F5$}4Q_lklUjlY5o*6?kSHW;=Y^*PLP=(4f|#^P5nm zwd{_BeoFbDX}kV-U;p={3wUP#jXvh+LH@VK|Ayq!oj$goinh$KFb?d^t3aG&B!-Qn zXK&3DPL-3>a^&)#OX+gtLLReVogrH}ayNft-jPN+V@4h~jZacVI7ntM;g??5Ij?_| za{UkVllPCz0r{k;f+EbF#uS(BDxIi=%$)4W#ExFP{)x>!`wV~FI1`sUYaZ2F2&t-H ze2+9Txn)i9GZ-!2SPetb3u@*_qt`6ScN|i}t|_5AW}2C?-^S;99cQpdUK(Uj0N0F| zkSw6Vt)+j&8q=XGxlCoLGGWcKB;!{b0&U;$*C)UGTXLf@LMuj zN-Tc+fd`E?^qu=`)Q3s_iTqkmFl!iIoIpOixJc;#Y4~1@D z^fF!U^3&qHEj+vkw2vb1F?5x?#-T zF50!2x5!0#t!;!$gkoYyEJ=bEel2Y5VO~McGAEkWkgZL2#+nJe-JQFu?=f^XQsIBT zz5e=wfdSx>6B=6O2q&D#*Y%+k7m`WE(iN6Z37h`d*gvyZ-9Zvsp#S+&+*q|TRAZ(Z zsUO!X5_DaqdtYm1s9-{p@n!SJRNCnWU#6y%WUk=6yVfGJvO~!*IgL+$%IV~T5R<;+ z_@H;=^L5sYPAGIhWBC3xvL#SqsS*}kZap*i9HRLmS&3H zO;+vl52(LqrS(7s1>b4p%>JEW>z-2Co_n_z3q3f$i=#w<5A4&Jo9990De$R)+VzS~ z@FNgeNJK-w6KU*1xP{u-(4%V+Y#1|xOGf5qFK$b`G>KCnrb2B11$9IW|3(-UTT=2- zMc=(^@Dkr+RMvNB6l)oR$YWO+ca4=kS0w(hhz+gqT9duHwrSffU_u@XEXT8up(`D_%9gpXXVv zAckDvu8%gIMz2E>6plh9s+VjB2gv+2ZC^;hXQ- zsJesbBGA@L0y3fB4NcC5-fAG%7$M{1$iX3$jGJ!Lg_A_Wd#H1R22xulaUGUh&x1bF zOX1}hQuB<`#VuDk#UV_;b%jp4t<91#$h5szsb&qoaKS!Ki>;UM<@0xPV9niv zD+I)h_=-UL)y7o@J$A985wFW-MDI1QF+EtJmCVn5Z$y7b1+5W<#<<@s%~>KVzb!>Q zC)@P~)38($smNvFX6$pcUDy4Xrh?a-vjy=LUYr`HT_vau8Sy2W&Cq`pKkgGaR(|gN z5Dzz;cb-8=2*d8H0OuDp!rGL1`Ku2))+6vF$Q1(2qg9?=PuvMwxz=l!*&^@kmH6eH z2^wuts^r5WuEGv8v9-)He^yuXNK~G?RFk@zm~O9X%g>YIqVuS??($F-mX>v-osUj> z_K0cxz3{kDCguo78_nn4itub)NxF|%vn1C1^}Fl$WJCws*6z;3#saE;fYhabfMOWJ zmdTytRPAWoqzYS8Xkn_%{Y>A3rdk~d?zmF(r z-g%)a+{nY_*n|d?(4Y*9Bce>}zj7;_8WC*&ybqSl?axk=>ECt)6iMHF>^`3(V zzt{#|a0GeFLLqH)^^93H7+Y3-1?WrI9R1xh`l%f`lJi)$58KOH#(#jcc+$t`nnKE0 zJ~UcYnKpOunwD<{%)Xmdz7mV&8ka#L>d>ZVcrBEv&ruWGi%7{I{dtEI8Z~ZURPZi_ z!}CGdE&=pQ>{6C4f2KJDT421JSO;?oq$iH~8$Q%ej9jAQnl*CXLf&s!6`LPtYE~N@Ux7hB`NZ+>rCB=++dDqsiAe*t zi0T}*1)Oo2=EF2`2Vaj8gD0K5Z2US~8ndBfzHfKgX@frtPJksZ5s|X6@hQsoydQGz z_UKN*!JWQpv*~tKJf}TOT+Gqk^q%p8Uo`UGL%+-q#;|bBA?>rYX({)tqa0be(<$^P zMV6Vy*n1~aV1B8_d{;IIZ>|#2+rO~pOV;~5jd9~=(7mnuC%(CzR zwD|5m^}s5^R5q(4-KgYFa-UM_&Q`ucS02RtaxjWr1qO|Q*8@i(5e_uaSn%xR=5%_4 z?s(p}wUwRRHo)@#+JqmE5wc4?R7!*4*)zvvC-9QTxJ=7R0}pjvxWmKixiDSEC`f#m z&bJCq)y}Ghe}7qzi?lnp*$`CeDl|1|Tw|?bLJ_lqMj4Rt1B9aiVYv||8w%d+m|yp- zuYG~pgL}3rD`?TCUJdSMtq@BCXPRxHrFS?)eMd@&;^dKQT@D}Tet^>16V|6QC#f3I`L5_jawv_E-_&JV9BY4zaj#6X zj+oZr6N``3wNa8+=g;$I6^PhfconqK`*8Lm@=9J2_`{q-t$RlL1dTY`E@Ao5sJa0N zgWPnW!wgyCwD@X2E9uD`e1ccEYhedLyZ==JK*2+od%zTQ(^SDkWx4)F5`Z;;5e>ieEe*+l7oOflT zb#!C85wT+8yu{*JSY*i7&kNrz3JY!)7;;=kpAO`uS8m5v7)0XY;O+7(b(Aal;#EFR zY=R_BuS)m#sLjA4@c*?Mo`3g!6HiBATfGV*N2?iyj0n2cI?w{qXbP4-tKlmZOfpDXKF#6Cj< zVab4*Z>UwjZZKqQn>l0{y!b|0g_zEH0bH3iErkK@Gs0;wqAXtNGKg8KNGz~p51CMR z3J+gxwR>LLo-2tmyNy-acWdLBzaeW_x7FYcPm@02lwsiAH1&aErTOvxT5Jr{CTIr-ke16!;P>kxmPrY^~;v|7z z0xVE*Igr=dQjKIp8*+hY<6&63fNTrH+7FNuQZ1xY30mz9+cQ-gm+cUzlgI_Kr|M(z z!7=oeE>Q`deQ{5EF4n1u)i6e4zWW1hOaNda@B{WHslRBjpMU;24B1@%Cs%^z^KPes ztxSmHJCjqaMn(lPfQR{c#uTbze_Ndx>rd9?g7SMaTyLa_=rH_6#xx>uK>7i*y%-I) z<0SRqq&aOX0rW+DudsI9SLmQoI-+X5E3mE_@~E!Bh94lNBS7j2=-qx4)GSdo7BvU)Yr!ZM}+deSotT(shRZ5Yao=~CNq8SD~ zUhlZJi5JYeMDOirdo0dp<~&xIq_-RPp`bq#zCMJ~gdeV~h^KJ<6#lBe#{=raUq874 zw{ciUPEVq?+n&rpamY^f{j>W7|1P$N+ylr80_Pn79T3z514^L;wmg-$2*8BaD^l9_~_qspZNfoul!z+(?^# z{w~D|6;~YKTKDA#=x*6zXHPI_6XKNmduU$IDwMX14LO^=OKVC9CUz)Hy%==X;ZMZs zjl+-0NZ-G*_Q+`d?B47euNx^4mwbr)ROF9=)(G;sZ7GWV$xih<#t$C;lUxqKPJIUc zmC<{Uz2UUgv>zdR2sb>U`|U@To>sWj+5fui#0Bc-MmO(@ILLm*m|ja}JvwlL{^g2J z?*u)oH;|X25%3M?Q2RNNp3%>sV@0Bb0X_Jw_FP@Y+%Rtnk+ubR6-A~XM!hkWy2wJC z&zHwGry@cuN%h$@;#~A{CtA(-WrF{2uxs6U$WGfd+thPTT4_m^P z?=O7{Nz(DPzlNpf*ni=&1zLf(>uois*T$i0=>v!|!oQeLjodOGhBFzpNq4v^7qV91--AU-_5^IPFtgw$HscA|bJQ8!E8k z15yK02_BaD*)qs<$&z&>q9PrANda$k-I#O;1+Ow>bmS z^DRLNzQLt`2o6Z!|(&73awe#{k2sL?y9yGx0Z#f#fq{$ z${z%EPRA(tX@-u+>NxskpbKs_Hj~i#e|d#w`Jh9EI6L!H%;Nuk{m`=kqUQ{VUOtD~ zPoca2XJr3~skt9k_PYhF3}<6ozI@MloD@_R_4o+Wd@`Bg;l!N2!v3RLj)yQ-rw>Rx zFwHj$KqkpMPG%lBZJGSkbNIr{Ywdl`F(#{{-Ff*9|TT9`d$`SvaT8vsrfcn|-BYbv-o z`RCL2MC3W=3i$OUGva!yHaSb%UE2x3ryoSHdzP_n~BX)?fJmPDn z(|7Pu_16x;u=Fg0(eCG!mD-XiO258#JHwx4>cOIdJQh6!d*(h?%_Oy{2nE6sB3V)s_8@rsc+Av*mJR&YaFL-9K+1 zPSPkIfhsn2rF z%>I;k?<2NlRguaj@p$TVvth21hGR8;sOn<(!iY)FOUt`LZaj+A1DMNz-hf`VLwC@;% z^$pxFW*f=a$&G<`w6T{AQl!@`|;ZI8u%qaYX7lUETA!etFLo+ ze053(?8U?Ja)wSj!tM42xwgF@KzS-TW8V;Wf){cO64^J~okncfH}jne zwE?VmN1#)Z<5f)0%CZ-YeAL*1ygOr`hzbxkJM5^F^fJ4r^0}(~5>q>(UXYU0N z-W2z7O~<hHfV@W~f9e-9n#S&8Bw~m6y+kYWXo|Udfm?#?R~*yz0l{LPe!M7k8~YwGfq}gl*|=|EUUyJ^lL?x8UhW z(vA{}bc7aIoOV??C!<+SJXDNRU1-(Iugv&f(n>^Xb7V&|LQJ&Gh;SBpy|k_GvA7V1 ztyzx02wY($e`25Zcy{q3NNZTgxY>N?v@S^i@2(?#djhiv2Z|*i;O0~b5&7Gb$@HhM zd>Zs?=u3En@d#UqXFa%70sKK?$g)ZrWQaqWG_(p1no!HGeDi?K>m{&Wju~SQS_bO| zJ)j?X&9;#G>zwnFVkJ={SDce7v1b{;3^EU z3Wj_j#&aKD1)Ozm9RK0WEg1hnbZ|!U4x0dPy&IB5)Xn5R!q=kJN!#ni&SakFnIuV&rHTeMi00FBa|yqHAYA zt<~{2l4zsA>7`1>5U6TIBgub6OO;gW;1N%lVQ&E=tn}GfKC<1?<&pZ(XGmno(}PA* zh(&04H?W4J2m*}fsj!}jtv;iqJ1U>6=*s8MNMayv3Abp6XoohUkoq%Q1kG4XA_Qn~ z)EPb|o;uKg?~m91J`P-m8u@n+&~M%+>$$(b!l7G}6nne;!$Q-*O(`6@30SjS_8qDI zpZdek#+j46rBlE@Q>Y9hbl!73cro>srug~6k+QYt0*iwDrD7SYL34sNjFV0aBIi3x zM~E2J9%r}BwXQ0(Z<1Kie5fRpmug(3RG|NOi9<&8CE=K#*vR6p{~`g96`BRQ2kNwR zH@wzTB0J1vDdPuO`;cnLI9U|%c1ZWEAV$8Rj0Qu&kfu|lAQ@WUxPb9K6}D#q#EKD@L@8!PZjQXa2}y$MelOz2yPj3?y}Xf> z;tr|C6iOO>Z-?j)kn-KowE1lZoq&IlG5Z{`^UzszN%(F0r((d-rNHMJ>v$jS{M*^w z`-1ZFM06;^Gyws!R(-0icA~?!4LIf7fj&uzGZ&fs890MX316W2Z3N`vne0vHicQ8y`l{PV+;#XDS!FZ(m2(RZQ<>A3Irm#kL7Lo7G;xOx|B z3i4KCpYnvg>7lVE>aFkgaV@EwE8$MC3a-42=GZ03SZQi$zN`>S6SLmMM7>h&+j+(~1vK?d?$orwtJzKze z=%YueEUWH4AFJZH!;l#i=yBX>@=apP4IL3i4bALe-$ge6h>Y zj6BqF`l~u^wO9)Yv{5v-MKrCX$>nwLUkLj0Hm@HW=NacrE(}jF0p=#2BWis<+bSrt z&VPtL$8fkB?bWxn(RAgNCLE9*!xG~1J3D#H>b;!e3r8sN2b!DOT6AW(qodV(rI=h*M`!wiN~sy{m{-P{ zLClwKoh-~8VH_J#BqEUyi#DxP;Q zwP7~pj;ioYoRyl2^Dk+VnN4nE-xf$=(hw|LmQVLyFJE$Q>9w=1Tjzq*@Q^gcv26{oku_)Q*qnPrNTzeC;H9RGW$9D>WfVv zuUkYUuSLcz20SbhMk4Od6a_}P2vy$7+{oh7oQ%t*SRC53-R{AJH<9{J_vt6q2&mY0?4p~htE zp^u@X^A1h;FRuYFU_N9Q=heajM<<9#fYMTJ9Si_N{`!=eaUcO#@sQ_*zI_^8wMN!kxhiY%b#Il5-o2*oZ;ZrHM>a zBhGfTjfU``jR|yjtv)XL?84#KnOnZw->j0eil60eLB$8N9+h5+R0RAo#1^$jWeJdJy;jP4T)+$NoouiTphSIT30a4ZQvy`LG|jEM9(y%z|!KBVRLkzgmwPm7xFv$?##?)#i|0?yztuzb3SbS442~Y1tzr9LuJw&)tm|WbSiNdn^qYwyw&x?mqn7qt|KkDTFN{LEM(t#~*m?4t!w*uS;YmOF-Rx z_FO^wZKnT26n(I#%<_}S>%~hOu!PzChT-YC??va_J>Wu&@zmDd-<{%7zxRH8`7o2l zkVqBab_4^?MTjjdBcoYc2edfV-TN&~Z|^}EgiG4ipBkl;H=63(_;kJzqzN%sS+UEy zZ9LdU(5e@j7ZF#LrB~z<{~l71tnxk6IaiDg<8q5!z!VB^a^0<5*o7H5J>n&f+x4u_ zk;eH^rTH&|FR%7oCx7CISz(gA!iFuiyb^9`r&QQFw<|hC5@Gg;@p;Efzqk*mq)(XQ z8K^bY&ry<9qZo&K$#e7$cCGK}iEjg22sha1l1`Vh3;?&zi(>om{gP_6iT8yUefQzm zcMF`n);T`S_70@^0TK0#IM!Hk`R+O|F=&zqYbnjEM9vP4Qk8gCEEHAd`>aRd`~bxx z9E94gug)vlS>g$B=(}ss^IKhEz5mqI|BZ>_8R%90Z3Wn9aPXrtXQ@2jZ=oO9uQbxU z$$7ZG^>RUWdM0_#naK?FJv2R6l7A__Vy7fA;pmcwON8FO10*mUl!b} zBPk_TBb?5{8^7}O#ig&SPQDS??3!nYtclK-UcC>??kb~i`i#(;fp3cVitaG255&5; zKHcrmdB?SzQsesIl|=I>N5d1|dBDZ3wBuH1rD#v?o}D=KD=#j9!+8F)liL2zKi`E; zb5#441_m)w$Y{<9i;z?-^y#eXVXwctCXFx=-1x%jL`oy>mJm*90E3;|_ERr94V3>bFqdT89ZtpeM5cM+{lYv@pnJ?;-79Ak+k5p-Ssz7C>QBF8YUp0+-ZIX3LE_YO zu^%&%ULbY4`aW0h0E6xeRMW{sMxG27Fi1PWK+DsdEV6R&=RVz352Sm|83SpliEbYy zI;$x=)&0of=PKpiOPxLKm*+}x<&n(~-G(TJ_@W+9A965H^Z)vp2PGzGp!X|R^T7A( z7XdwR2Kfa(b>R8+Yq6mJs#JPz_xF8upnyPL=~BKxtrMskoFf+5p&%z+0O3l2X7nk( zA!%E7Wi^=K&NlfL%a5O1px3&BNS{0MqEoI>zP^Df`wJ0`;fy2+!h)BrTq;;bWNtAX z%cUfpB+8peQr%KS% zUzc05Ws2^GO}uNEsX4LQpwZ4f(x7)#3ok%ZparWj>^S*t(68Lo1MBl)*9eHe!*opD z`G5@Ys5Vi0*(Xxiq5jf`zkKqDIeB2@{+Z*tZw~*px%=kr|Gdt-=jeTNcz9h(*DO}^_;%-dvFDMfD74WKthiJc8AuTB%L$2 z4x9E*XGN&(R_~<|$)dq8@YvpzcJZT(Upk<4{8tP82+MomSrhWGp&~qglVsmQaM-iI`Z)b-)BYFM?a72GM{v!PO*?hh z{2LuUr#m6z?7p9(02OLwD;IgBJ&$b#IsaICxg|p!S~vk>CjWSt4ekiC{;+-1z)2VXw_N?eq9?5MfF3k04X^!s3 zWwSVoRoJM>IoamXe%K~CL)Li~_@Zc@|2$?JO+MBl#T z=2JOW`vasD?&V{%q|?*4jm*VrIHqkLuCmWIW%&uZ#L6>@n+#;0_jIy82cNC^DoY~3 z6GXbx>HV86);C=@D74=~)S^p-iFu?%k_o-Osz=Hs!B=fk+2K$Cf!#wHs@A1GYx1vY ztD^<lo z|HF3~_HKt)A$=tlFIZS!35h>kc7E8F%kRn+zYwjLkJmkIDhipni|@UgUWUhQ>GNGk zNCLe!txqwxE%$&QWLNkHKj=F{A)&h0YxL3PHghpvcDa@Xv*I5hW-!UyC%Bapf&Fg| z{#d}7V9&@}1nqh-;Ja%LxXyR6BIS7>`1e(LxHCSNQ^y*Ozty|{>I87&2Oj{Lxu3}U zKLY3iI&MfC;9=MPB3}#6?z%5>8{hRWK2>AeC86)t@T0lJ<0IM=gdz2&X3aOqUyTuX zuh?hs<<7vbnFqm9@X)AK-X(C5C>O@YgCmsm93I*iJlrs*1@AqZ@2-|8T@D>hiOP}O z?ui8q1vU;ktG=YVS7$$3MX7Z1)nj!u`!7*8gmdEawR403wtbzUqBOUXjw4$QhH(VXe{l=B7QgPA0YRfKEn;b%vPdb zCcS19$UkJ>MfLfH9Euz!hzmtNzBP5CgK3?JQp4Jaq!K?YY+R9ENrQv4x5TeE&A@0O ze_U2uUnz|De2YXkkPe~F+k3BbH_4>zs}@_V5dtB(3EuJu(}0@8=)J0tHe}x33Hb%F zfg2ci>q{ho1Z_AT6dPv&pY9gZ4l=0r8#|mtAQV`KD=+w3{wRjN*>jrexhxamou?EP z3~gy8oC&I;pLsUCnv%Zcb!prBvan*FS+W`q^K)(x{j9@AoaRpJm-y6uZPM#ebDqOg z73U24mr2g#&IYh8$<3b1KBE=2F=m$^AWCt5WGP^MZ5Dj?>8l~-YmOJ*K^*N=J`x~8 z0@^C%tb;s@cx^GE5EG5-Pu(idCK2R5=nfnECU|APCoBu@N15hKmH8JI{o=M1(SnA6 zQ0nPIO$kbid@~{P!26{1ZU8 z*FIt^jx;8iz8t>EXjTy_uBiAL?~{IDY6e+k1s7&aEtNJ zk<8->bmS`|7AxssG$-*R4YuG$exHziTkoCn zV3Mesh?)}5=TlgUZm&WH)A^XpItVlF%z51RI^$|ZUy{%Y38w@o$E0#Bk*NTNem?zn z(C5L9AD}p2f^2|721P-TNVb0z^W=`gg9|fCun-b4&#K9`8#3++4h>@Hn}(Q+^Mk$T zCj%Cr^%81bxK)e;oB-xA*PcJ~h770UfR(2=n)|9UrZk0bOu2I@c z4(@jKn-p!hB`EDmEv3a9>BUNw6cnv4kcEt?Gd1F0q4|vYD)u6_1dF&0nYL=LZkOc- zdpD+Ef2aKsBSv%5y`>>r;OFY1c{3&a($C9z{G;%1)Hx4V>cj7xmxBkBDlUWw5 zTuScOIl2&?bBh=K_k}wQ$p!To zChl`4N^ei!@4e&>v|72ZFC zfv9#iDHZ{Yb4-En>tg<4CEYTufv_}7!)af>ClkdS32%6Mf_ScuPLS@#6yD&YK*(U2 zLN2$TJ3tm0&i#QbYB)p|$>`v4FDbqB9lniv1=ed5oddX#8ZnR7Rp&JnR2#^Zf!e8; zD*I;H8Sx}x?W~>*&OUbCX21=IKyoLop z-f>=+?$66zJ=zlc}J?yi=b~jTV`! zze6X89LVpqON8dk&nq|H+$|4$lkuXUkt=bMXmhO1+dp3t!35-1SSyGaOrO<+H7#*Q zy{5nV75B3vzFw#!2phDck~@jK=E1ppZJC^CRFo>9a${<0l)g^+`z3)HhTa@8EQW_! zY@td%7^aK??^ng_RyZv=Uq~`mAbI33?A{r!oi``Zg^cQ$V^p>?E6!u{)V4?CMD$S1 zv^hCdONd`zR^7-Sy}Hek-dFkq#J|duJc6I_?HPIS*G*qyBqu0fP>{g30xRj z_Ad;+IY;99$2RysRP@hxD>K+p$0qEq)FH1ew_U?bQ1*Ri3dBUta)CNez{1bmgmpeV zpf%_zdl;?1`-9yZ8svCrr9Sw2l$QQj>jMWz0QY;+Gv(yRW5V_{`S%y)r}$HO<464Q zq+qZ8z>Mbqzx)rau+s{yx)M~6%r;5_>i^!(4C&m1GnJ&rMf|3!}udv~Ig7{Kem zR|nQlQTJ2Fj(n?SPCoCQQ3f&+{ADOlE$f%AoVjs~-t)M%9sq^?0A+dnSF zp=;M<+7&5gs&{68fTAz{wItdT4yd1rp}TfXKyq@nNo+qX8^8 z|IX+SkN^P_=n!G^pML!hveD}SDJd?@((UaUEyvbKKILB=g?jHVoiVzUsLf?qmG3oP zAbT{-8+mVkrM-*Hf(};w01@-NSr0%W*%8es?AtZaee{XAm=c^)mY}rILsTxr*pC`& zUOG$B5QcOxgf6cB0C{V0)nPXf)x~&bcDJZlhOzJN_5&++a=)w|*yO|{up;;o?e;G% z==zweH3W^}27{}s!194c?dq&*{QyOUMeXbYUc17bis~En|Eoe;)&skFv*s*Vk6fF_ zc8{y*400&~yPwVaj(EoYxSDiNFIrW+x7=HvPQ$^&^Kb;nAJ_nIOi+?tebfDvZ%1Qr zI8^(0xRO7IpyhC=_HTPDc%70vh`~|eqmcs90yBuS?=5^2$OV*0{2bE<5gy!y~XMrdm5 ze0%`02sjkRzoSWhGbJZ=`=QhTnq~lVBj%>oljKQ*6z=rWC*yT#PvYy+jHG|7S~@A7 zs(S4KYT=$D*+c`NY}olX0T;0H6iZ-+6Tv=n9Q4?+BzHB)X=ntgf7RP z&|yFk&i|QqI=U+<(w(fV$n&JEq;h%0&#BNg0zoB*Jv-JL%~}6cDf}e~nN*T>VSwwt z;<5c#70=NoeB5&HXtf;5??4hV?^5B?{fL=gJGcL0b|@o9y7uoVtm7nPV)6+t53n_X zq+^i1;YFE4W%744^y!U(M*%j&yFWk$Q4OE>v~-u!d+CS*77})Ij1)GL;YXV6blrXC zzz9^vzjj%sLnm66roXlvcw!VnPHiR~ZJoan_)`_)X*%fi4f+46Qh&{7b-0@V`K%Jv zfP7Y%`}wRA`1kW!VMY;6*E5twC(RHKZm5=!$W#mOJb2Oy5G<6$3s)(;Krk~C2^;q^ z;=utu2lUpob&UApBLBN(QZ?USRe*N*)BtclN{jQwsHc*+0!e!bfPc+=as{o9jNh<-N>u5icSVlFrQc3H^rBeq-&G)9cjPN?}KG-IEP-PpR=|kcsA`R(w zOub^kH{xcjEZxsw3N$t)vIdYGM+g{HXe{Q&K~Z_mXaoW1r3 zc!KqReX1rnBqzx;37P{zk*$C}fChqIBI=dN$(O2PKO3ylyrz2Y$-z=*^@x0~Qg~Fw zw>*UP7LNYaNTOxhB=f|9(s1QCy#5-MaL0fcf6;F>g{T?tF2a|iT*sDq7u+EhOq$K9 zX;>ZWC13J;A17U|k|lY(0I8-!2&3g$L=TaWkZC{aC?WZzvn@Uz*t9L;I-q{_N@Kj{ z18HksB9l9T^Wm+EI{+TYc8fA-(06^MuRUp)jBnpZhvL#KabzZDucI|nbXNQIT%T5^ z@6;&N>S^6ynJ5h+IaF>$sTOvAgkD5E6ITZyb~8q|de?7UQyRPdAS_IMT)yu{vEwMC z9IBW2CmrPVrP7olHMi$2>1H1nHoR?3ua^Wx_k`8c`*HZmMPf^hI=2}=wIfSp_KoAQ z=^L=}d{XGY!abYA7;rDFq$Y&@<%??`ju*Pm_(hbI@~L&L?QA)hm98UU_4$6_iYtpl z?m4AmkIUM}vm>`L80xwErqenF12-6ApM0;W`;Jq0pPKUZE8&ldB*wA&kwEg>!6t4rgmlQF5w5#p;orPTSmNgblf=0ygja0I?AyCk7~V z7H9#i;q!3_IB-Haot$^}@U;`bwH{?Q_{$B9;OKsFoQe}T&jP5|QxLHK3CC0&@Eqi0 z;vWduiZ@cg0Yafh+V*<*Llxx9&TA_UYrogyqbzL6T_l6wt>xje2-7#lDlY6MF{l`_ zs70l*QF#B1usG_mSU}p0XL`vC$y|)!l}8_@H|(~++!}_)gO&}l{4Ndf;t}gyRJ`fl z%J3^;1U^dynmCcS`4CkX60v+eyF|DiDH6*#QM@aHh0uj3c^U0+h!-ydDPw@>J_?J1 z#XfsX62-Rd7U5{qGT{c)foxYlB69Gd8%r2spiXalrm{x>YgEO z8`N^l$Mj1uR|)X;a-(zd`X;!REQ6ovbB^YgMmR1795>_{)RMZ&JGPG*v67*jRd|sZ~)d`GFD7xM_kqS0CHHe@%CG ztIQUBX}D}+phrcUkh1}zFwI;-F+3b^^=VPQ?Us3;l}2%Xk=3UwbCL_s;erl;{w5BRdZaSmq{-Vo* z`UzKQRWWwHvB66#$om*a4Mp_tqP-VPVvT8rv{8iPH;R6MS_6N8uuF4h)q|4yZ24=E zFrQM4XVkgSb1GB4kpcEer768{uCV9j=dZ$P{J!_0-U^?S4)h35A|g6VekW=fcM;sd zx>Hkf*WT37j#abhP3>71g_+?GEZ`cHF~ZRR;1#b)kT0-VRcCwg?K;+Ngbx-=xkTH7 z!Wy_om3M!QuWa5GB(GE`S3l2qe+*=*b{}CG&%hR5RcjN z^Q}SS9` z8+fo7q}WDJzZ9n0Hm_acIf9)%0>!lQ2XCq8mRT>vv^9Ny!i3$np>;oug@!buZ?!hv z=mElY9c0_xbmCMQv0e6LB$^ zPXrIAK2e4_eKAF%WChh$SeH{+4=r}@EUFQcSns}XagR;?bROZln7`WpUaxw zXxplP)fWChBQTI90n5Mc;+zBadCjP5qv-lXfl($dCN^8}1y+vUy79D;Nc0+Pf>4ec zKe1bNifA_PBsso@4V%EHxZcR~fySpySNb)Fj4=&Zj4TiPZvr)?SO-+l$~ zjj@NKumaG$RV&yR(Zp$Ok*w0PcYa2RA^GV&FP{^9Gq_K92^+=FBw=DmJ7ZZWnLH{L zq1-bRssMxOT#hZYdZQ`8u<-I34dDZ}d?L#AGZ|`+mVJ`atQ73Ecd#+UiN_NnQVp+L z7Smp!#>TIT;Zx%Vj7?U%gSQ$=N4&8o>23ic>8c;yl6@v&}W@Z)L%;cCVd8GEyyn;+0^nqln*@mF7TBcu~ey1~!VmiPfNT zC=iNfJncZ_G1DYac)MSmHip{zi#rY9V{2_w$Letl!JPt)fV_x7$92v!f@fAQwX?3S zs@0lhT{XJFphn_&p)Y)nW4fy^G&=g02eWz9TehN0t=>7?Y@Pt1yxn2APxq(u}u27lqhLs;v zFI2V3Md8~DhCLM%xrjN2o1=@nCyhs)Z@?;>oKxiOax~6fG^Y{)0IIB8vd6qB)y68; zm+VzbbGe?NT+AOvO<-Iq(dO4~xF{aq*0kETgv`GVAGAaUq>oreUTy@V zYufz9=#`e2;(X4Jf+P8Jm&rEWBHO;*?FFleQ^f@|aX*lu7+vWWLcH@qp}*f~T}4lE zMz=S}^F^p;FS*|*-R9^!VZ0LHd`mi-G6XeExW7^+&H8Mp^x*dVmIKT)Xs`=9J7z8? zE%=w?(BY%>fAHw60|`Ug0H#Dcise|~nYWdHX9##9HjZRo*6bwi)9vhybdME)V4AAx zaaAc(wqCvb&2vIHh>HD%&WLp5t=ulh zTtNj{N9(2G2x~k%E}H~+L5}nTG$h)tp7M<|Ou+Mw2w-Y*M&3Ja3gG{ZDT#Z*q24H1 za=qWqZ6|e6Nhx?|`ksjG4YlY@ef5F;Eh$;LAxP znh#=3a%w6yR_u)T(SzY>i@QV1qBg$*kKA`|ZMGp1{G%%8vquK>1)VF{g7p(r^tkME zu3BtfelU&iPW`^TLM4zgX=;+vC%j7x0vUPEBW{{J)q0$PV3pwodQZy%dgwS z_3vww4IwYo78CZCpjBRCduxC!;~=_jrCG(yG*v3|1?YZi?CfHv0`TnbQZ(*xyZF*v zH464ek#0*GO-afNKA&1hejT|D1%^YX0yZdlF#e)~XQ+>U5ZAUg*5+UMPLmZ?;+9Fi z*6R%$9p)b}nz;F?t%w++=PfxOwzzEpyqiIOfGVZ;vh9C>BnCIX7HP&9cwl!ww{E2&2JZm|yva5VRQc+)NJ~x%G13uC!p}?uYI~qMs z_}mFCdN#DJS1#MKyKAsWaS(~N73qDlc>X*i*mNIq5Aa4KQ0g_9H&0OR7Xewn5LPc` zxz8gXDiPO~t)-U|TH1i~+a@-}`0DeGZul?gKn(_=ZTWjUMo4KJ|I`Tmh^NanOTl zX9Hdud8rR(r#(mE(;M)y9P0G3@2>go%f!B?|FIaJSP}am&OD>7X^Q=6K2a;+w*R|2 zq|W|>Dq;(8_aaq_F6T_5+whfO~40_&i2V0Jsy6+Ev)H|W$(bx~0_z%`3 zE3vyn^A8Z0w%)!yoo`+1D5U%T`QyU=GPM7w+y8WA2~sNnMiRCogs_f6GF9|w7(9>j zI{zo#{@Wufoe?~LVfB+m{t|`sqQXgv66pU5ar*1HD))BF~=YN|a@w3^BoD37;Oy50M*y;dm zF_b@}3u&9bw+hCW*^rteZ{+oY&ana;-8`^mwH*FacZ}~#?5}g9-PMN`tK|dBoRJG9 zN8`r9GCvys&$p$!wBwA%zDew~*@_o1J z7ga2S;C@!rnQkYB)&44GIyAQDfyUV@0;C2Qn&IJv(@t$3`ie$TV=y_K?OkZi)CpC2 ztfUJm+@IYlSt<)F7Hc)UwcyjG`}AU;0Fj?mb|EF+>MIX1@&eS*xv4>^vb^6-L@GzH zdVK<1ZHOOEzD|3rnwj}Kd^fv(fF1xo3;*5*hTT>%s1r5m3xqJ>B-?h(X9MpA`-iw5 z;CPjsa1u{&84`7HzI13pwB$ke_s^Y!U6vpQ&pP1w=+5r0+fFPzu8)6z1Kx{Tjyd9a z@hnBAj{4$Sye=DZ|0li=TC#k=viI#LiAD@q1`wPI*DEMe|83!FUi$+;eLUr@%(Rr1b{Va+}DI zs~#`gjB@rK4;ze@WajyI_ciu(r<2suj=x}Y#k^gCmB5LUTK1qUm}~E39c)d194lbj zyZX+O#4{I`04Ia?G^SKoeu@!Yn@m3gI()QoF&a7+>nQ zjMcDaBlix3eB8f*ueF>o?UBO z=UmiqA@7qd$`ZIliSHrfAt5@68VUdAk{UXbz6Jfzo4eT3H<`Xgh7HW-{^4m3xDb#W zSIzoONe{`;WhXh}$#hGqd5&?-vm)0^Mc2Pyly-{xUFORIfsUFgct^r8KVfB(F4x1E znY>WfXj9(s8y*d0<>fHbs#mYPGP9F3nw+-btT)nD3$=h zHMj%_1a}Eeg1ZOz;I6?I!Civ~2<{Nv-JPHbF2UV>@h0g`ckk}r@9cBm-uIpRe(x{l zn#}T1qej*E>Kn(3Z0)RZ-R5eaB(t@P5qg8mz2LZn=1^Vjm5acnm-@%%di`Fem=$ocFc~S3Hkm>zx^;3|GrEAU;G!q<~+yqYPI+~Ho)c_ z$@Bvx_cwITeCJgpAR*%k(JtY zXBf9)Z9#bF%syfmqcx}5_V#k9h*@HU00?dQM6~~pBY@(^%g{6%IIhAlxXf@G#HiWWD;mqg=$= z0uDvPid*(SdfrTe>KD%Z);VsEK|`SK+Cpxnmb0pGvr?6CvK}J*s)|GGhQ~2Rwzl>T zXxRhejy#t}OBLA8?ctAQU?sHZ+;J^I*7Rx1&Q%S_F!q%GR?nzkmV~}HulCf6m#TfR(VbS>DBOn}F-=~J zPV{GWBA8k>V!-BLHeTB22i7|+SJCqFS7AVRqyasaIcREB+YO{AUuj?8@mF z@HP*oVq~W?+3kz0*>XJT%?Q>b>=X~Wi_W*f!)sGJqTV!iWlR{H;kwG4SAo4tuE7yK z*F;&VC?29hmC=?W(hR#Cab~%Wox+T8IK!7;qhS>gkXM@QZ=N6AggoGn`OB>UH(blb zXfYS%*KhVYDb){glSH0}h7m>RN54>gLxxzyC^*B(W>w3UI6ZL!e zd>i;|@^jNSg`H+>YwAo8-4kgCp}_JH@9R$)n>NC!?H=6BN(#>ca8JKW4bM*zW6Mtm z0Cfq#8qzHb2tmZ}GWGzP`6;29gP^NbN?<&H0HuBt%Aci-K6v~8Ci5Q+AvZ{_iLUz&89%8~zaI{P*s`>s>Ul|K4|@8d{ov@t-aK zzuSj@yaXeHeFTXZutwnB48WfL?Su9EjgOyM%U6b-`|?SpIo9@HN4P%V?|zQh|0YrS z+ade|Vep%{>s_b?(sMT-KLW4jv;(EISzs?e(lqit?N-@%Pj4>V<$`UqC}M9Dw6U;p zEc;}@@Ufcp(7nu@54@hgiw` zesvGQcflVGI#Q;~$*76P4Tksc;zFvaBLYuMiq#l)=cEQD zETR@$(HB=TnZs1zoP~&MiuhG$G)odVK-cBE)O5_`lof0}O>)XNvLs49psH>ta|`_b zA(S@2qU>YdJirn3Z5xu1^6%!Hg38UoCB-%7ne%bY2H5!Np?`W&l!0DC^Q>S_KLAHI)(j#9&K#!#269NU7Ck! zuB0Qa?)!C-Zjv#PS^rm5$|UbUhTB#iAnc%BdUA*0oWbuoG@0>=ihOR4za!fXevgN4 zxMqhZ9sC`Y-JjY%LhUcDPB*PN1B~9%1YQSlby=VE%D|q22EGoWDPF`3MIzEfeNGB* z5&Nj&JKsboYj;B`p}AuhtdM11ZGPk(Pv#Ki@v}}2^}%>XRy^MMc5&Qb3Pd8#Z({wQ zcV?Q1|2o2cnf1HVD`{I-0g@ms(BPUUMHhSi;rW%rja%YcqMk#Lo;70x6UrQ!4d*so z?}3)bU!_W*c;auz^u6Ejj>v-X-Y(dELt^F&odXotf4i3ZM>1wUk(<{WeE`r7SLXp| z2b{``!2e=z0reaLT`fPXhN6H0a?<{=ZhFBr1E}zWhs#Kf4a+rw(;eW)m1762)@ap* z2*Z!cH*0jcS%;P2rSut*MF-zdjgYJ6bdkM_@mzluf2PeR4WLN>>wV)P8{=-r-CMg2>p z#$)qQ&Dgu%R52A4Dvpctw=sk@(Ap{Mzh<5Jvx8nc)STT}G%vX>UW02u@788u{TNDb zUlbN()jQRgmoA#&8Fe0EeFfWNS`bNDW##uM8-!059t!cw^H6xuH{{4>FV1b(#G{xq za!DV@w2@e8yXdtM1RwAKyZTi09~Sb@ZZnB!0B%^{Ly&XvSvLbZ`>LYn**Fi!(+D0* z{!n)^zD$bYbj-MTbQdePGZ()~38&uAwcSe{xI0O2Lo&@=kz<#oB{rldR3*pH1_ttW zGyQUF%gd9vGX31M5Id1}V&x?K+n`N*Dt;R$lizxj*lB9ddf?XgtYze=hsQR8u?Q5HesG{iU;_13s{U++|J-PG-=G5DqqY>PS1;t;9T)&bxc&nrVd? zvBTPcdqyJaD&*W-L4+nS8Jz*8@?=N5^e_D?Dhblixb@GPKHIP#+NZbTQO}OjQ|ssF!}Y-hYCIxG^1AXj|N-}S!Sk(szMbZGL27<%&N-5jV08;1oL~U{p3=WIZ@jtYq-72K+V1qY=IjE9J@D60#;E>&QUEs*e(R*4ZTO*W z|7Q3e0Ia|^VZ-;A4bFc#;V4Q}+ggs9E)T#R^7jy1^~3c#zb6Mab`*`TRx_0HfsLKd zk1#zDrK_^orbI}iIg?ehQAqVRfpE-HcTt#8brHD}A0eda8&)(d$xV+!%nN7l1ftB1 z%uomDAFuOsQ&Y+J(Y87eioxQqBE=T9Am2*9qo_oAha77cCG*9H+^p~9k<}*x#P`!s znMbU4T_u+?7g9;%H%9tNaC8jnVk`AwQFy{9%J>Fh!5t*uX%HPrHu*34)5k8|G^?lS z_KDy&YHF)L%+AfsQK{)hLO4~!!+OI}OGFC71W%UoU7EyKxTJN@`0Z97hlG3utZb1v zebI0%RbhjR^JvuOr?kb#B~02r(x+DlEoXNijRyv6#0d01uu|9ZYILUQO|6aKdg$fN%x?t|#C!w9H=AG?SE4TL zJr@B#48cmNhIx|jco+Vqj@o-d??l76(E=#XqggIaqSiFC?>ugwM$DJ_(aq{pj_0@I zzK(_}2vi%k8x*ItlhtL?3JVYOu(rpnaEVu~50jB%5j8`*3ldXqc}By_ z!Q!+j-*A+jT{HU}8!r?HPL+e7BEmV^f$^f_P*bc;Rz-e3^PuDdO`<%rj~+h4I}xcIgJ@GrEVczn)V5!}A0mtEuFQ$Q`D<*2bGZpzk362=o;G{i@PG z#$5k%4E8@DJtcn&StEcFHAOWejzdjF+j$D1%C%&TYr4QPzE6MJTe6HQhL%|$O12!O zMF1h&-{0z!)?W^$pV#bL{_4)3L(P}H0^d@xq8izq!JM6=--`Z8q=Xr=jm+mwTtuOZ z^$L5eZNXR5daL*%4f<%Z@}}rD_e&oh@_m2aI<2imPC_PeUY+?9a8Z1or10f!OJU8X z)fSlF@|6^Lyy7r7Fnb)pZEU?bSHYJLu7K+V>$NGiD)bOQYp8nCjM1Iq^J;U)IGVau zs)ZOb^A0ff7nyO9pPaT)LZEX2A?4%c)ZFRH>l@=ZEz@tLqQ%K9mE>7{qh$pi*)HJw zOF!>eUau%vpB2}by%_8;n=SqlIa?8PAp)muJLsjTzO{AdehuHmuyf~LpBkI%Qo>c` z;?SfhF;S^85KCuPvmi;=Jt_&~lI1uDF+3*N1B7aNfkONyF(cBnKKGobMyQVsM9*J= zD``PseZn4p1-(0)dbwW7BUz%Zw)kdqyOggupEksrKu|4H&Noqqa!o)0_l!g}-o=!1 zoDajrT&;Y{@%i+!n>ne~G~Jrmiaf?lvU}k1L{ zx7df#{_RF%b{+t^1TMJQf|7v?rvC)d@b8Qm|EsJKnHTS1U&H$EvuSk3?(ONJzwCt6 z=iG#cKFgY7+hpC^+cT)CYo4&eY*cY7$Pp0?8#9cCsq(NCJy^Wr?2RGhB zw1+rp5C=a+a%p>C*0ZpMx|0#;VEIZ7w)jp(=%Wg@VqxQHSm$VjU3-5{ef$va7bDO!hitkh$jih7aa zP!_l~W9X=u%GKsa3P8tOQB-CvAcz3@*3601leuHH#Lw0)_3JP1AQ& z>xCQ2#q%|?p3{Sf($St2keMi3mgmp9fN;E{J(GWGa#h;(J+6n!Z$m$A>E z@pcpOyQ+)6R=)aR*Bb2t3yX0H>SVtj&=#^R*<~n7pc~3w$&04zptdXO?HA~!?sVFQ zBjDz32{}VSnAg%YTfjattk;>@ds}0O@T``|@R1=R`4b^75jlYw!JRPKM{_$IFH@VA zb+G5&>>R1RLf#Xkdo5&3WjQ*8EddJw{k}~_L6pss&2d@>0Kur9eEo1>ylH)@+Ep(a zoHhA4z=l43e4lTKd{Z!7bOvWv*Hf3nfbSC!5n{1~vQ=IbHrU-35iQGBG*2K9SQQ>+ zkwCx-YE{t(U1Od;K>JC4-}p^b{f5Q{E*DY#!xd<_lHdJN|4+HDqmz5e;7Y7%PYLaA zM5>*|9k~sCyL>sLVi7Y`l}X>z;0}-1IAcw9+FZcG+}^?7WHf?g#;u+mo?Qg;5ni^h z3YUkktn8nNPU!`^X}nW?E{2~u8+Juk)r%hl9i=c|^@3*8^)7aTW6DNrMyz~x1aAPp zR73?A#`AGb>C;jDF?Z{uC-|jLO`W>;cRH|DeI%RY$<4aOYs$iEDV!nTapih)r1i<` z!?UwpM5W&Bp7A3|7MY<6B4@jN(OBrh_*@zE9iBAG2B#pFw6SU<$Fh(cq%nIynSC&O zTz5){b+TSvpA(gR86Z=sg)cqEUwOJ|1|_qZ+!5#JaDl zc-EW`FQzkpc>w8Y#|fZ~czHWalduLW_9K}C_n|^e#yFq{AvWxnY@7@k_;-XsJ4bk` zPT-odUG?D>^Q`S8I=?~t%9*MT4FLm)g7WS$^lxO)aIVH5Rs1@Sh3V=kipFV6s}`8O zgcxPk=USs8Vf*3x2n@*IVV(Od^HO%{AeOjuA!V`5&o>ms?Q&>$or}vRlfqsUN4GRu zqj2qMXW`+oM-{tsP`c0pP{B_zq z6G_TWWwcLBr2+eHvIho0p*2`&ZnUtNc11L)HsfDru9v^%gmLA+Dj&7#Oa0wFP%DVn zA9h{%-D1a0!&W1J0yE#}pV;RiGbV4B{ZJ94*=xJ2X1`VII!(Id!7n=X{1EH(-05i{ zeN_=oZZvs}4Z7?zOMPCE<$Y%AkLn=M(ypYd);mw&Ec^W_;)$p=VvOuFVMH^n`LEOk z@1wg#0!8fcBDzElN6WS&839caoUjyX3egEXI_V`s4i?$e$M} z{!EVWR|e-t4P9kTRdF+i8k!nX+2p}mCE>YwB!_~hY>#t|vbL+A4=7cCZmQsXj*u`W zHvH1PII8P;RjJ7}rY&C$U?}CkpuyMH%p&dGhSXds- zEmm;NHk7ao)8ziqI}|b5D41z-oTc7en)|hP)sAAa|MeSJuTDc%Bv9yDLFu!uT@{Y= zat$4ds-a3B6wEd)99L8qwkQO zTpXBlnC6?CxRxw1niVHPyMHbhr~i&Tr7>wPT#QUuY=%M@f;t}pa+A7fW-9)1E>SXH zwh>V#)gwA>2lQI5Zq$#d0X1f$nHtb1WGBG2ht&KBuME#-%SHicQ79`TGVu1-=gL!> z2A2|YW=$clQWYES$tOQigw*Nus&<+5s`e$|FJ-`?!GQJ6HE^tR9u{#6)&M=QgW;{L zO<3rgXU83X!<&Sufy|cE6M{#)z5TRw?198vN~hAfP&N2$P;@;J2s`tZrNLRs;3MF& zSMvz-sMvMlpJC@exv~WAc&Xl+{ILklk5=W+8ZB^fpo(%yBIIQag8FZ{!5#pGf5Ywc06_dN@(y2jk#77XN#sPlLKOQp z9XRNTpozF%U+njy>1F+9P1+Et)-*vqfH_pzs8?1?;|Q^2Olb3hK~btXoV8hl`$M>J zEVgb5*2LNy=uw|u-ux;>=?M!*PCO6xfZDKWpZ?8UK(Q4@5Xyi!czePKKoksxecE6ni9jZ`kJ_fwY?l~ljWoQPJ>``h6dHs&|BG&I~Mf zOCoYm)}_7RNz3QcJ2L5$vh}-goaM#PRi{yw(A4G}-!b=>N#o}5dRZk!i>{}ya6TT@ zBr-MR61pl&6L;dVS3iHrUYctLMewf0uuj~)aRa|PCUoiSlr1%t`!J-pEH}6kJ1TrM zQ{Fe)gQ=ldwKDd6Li5_f@gBmw1`z&0&_S8#%pPzCvqF5ct1Ln7k$0DFz2^j+aUP-E zoxcdOg|D>(yqg6qwXR8}ifW+Z_|2B70+xo)t2E`-e@D3$7d%@2w^W4>2+ZGw@()yn z50K5j({^xwAzAxDQTq3VLKIu`2rZBp2?&LHcZ~hU%_R?-VsZLAE-80ey)O48ggXr^onYo>qMM#Obh3s^l-*1 zjt^#~2}w*Kx9P}xy}#=UkzOuaWlziO#zL$gSHoyoDa|)6?eTJ2sxm;^W?8rt48w>gcXU1pzt^s6FxAB7M5p36qYRS zs@jMBPA~f7CH?O&D^H(2tBvdxf@;zTKWq8Tw^`u#2-Yo}^@CQ&KatEDwts_Y{z2^J zXGc(L0&(R~Gf6(57P9@k-l+ufACwuaj8u}7w@PcZR{!=6%7KBo&%B7oyUr+n&>U%abt^C8^UrF zT3j$;ZOCAGY=w?t=jx!|%ntE{#ywb=gW);j&~-1m zyIe_P@dw6p|GEiRracEP|gkBKcSq1d_P=Ry5JyNQf<0WNVf19SUY*WilZ#Of3D zKM|*Yi=yK>!o-P18Eom~Z={y}T^m0A1K-oXL1>-Qc%H4;ulIQ84=-;|y8XtG_9uGs z-&xOyf8!woG<`AVVbJrzvg0da#*HQUWBnyWlJIowJ)=)U~3@W_LIZ&A<8+YV> zw?|T~!98FYOJ=elasp`)8ufyZvq#(96Fee5!8$cwy0=STc`jFM&P>)R&<2sd3Jp!* zR4~0jd$^uvWc?0Qx`kcsj5vUJqTLdI zF))-lds(ZTye5*8q}2ybbm3_kTh}`0Mt(Ud<~hspyOEti*!bN3txP^Ksa!)Wug5Cs z9xsTo(jc)&dy6a_ed{2g9tt(|gTV0O%AV#CO}_;7ffuG6=2?bEV$Wx@0z8>g8Ulxz zU%F1z)7NC1R~Or-Z#%t=E$0f$uBa7{WQ5qG5XWtUC0YqP!|p;LMt0GVZRBvmV-GTS zGCT<#o#_nMLFCwn=Kj3PGQG~1Q@M{JTNk&W;a3%>;e0~+4yr8Zv#MRp=yG81uGoPp zb^XFjIa%@GBl5`hP;wk&81Kx<_XdR=0UYMv{}W z&eY=f_dDq^MO*5~wIq0x{_%N%`0hgcIP1r=lg`jS`!8ZP7l>-BgJ)Ky$FggJ?HvhZ zVJz~QC_=r^yspUiccMMa-v$c+M&mC`dU=)#?c+l?)H7`xVn#6oPa5XV48pUn&k7wL z4ADQc^Zl}j{~bHuFNWx!+4+81TmMA-_lu4Dzm|20ahpZy3NaxwYQJON73>3zNJ&YZ z&f|NC-k#N#8zj+th#Y{PZ|I13DquM|$X(b2GDfL3h?Z3B_(bz|un$H&H9H{{;>^IW zxBYM`Ea#HH%~iW_mAHIH(-gthrf68o>6tUFz5u5_G+Y}rWyamEN_W*#S^i}XohO~M zYFU&2-0bVmbQM_=GH*V`J)Ifq7ULp;994SR^FCSCcJIk6`hi#VBAdJZ zP<|h5vZ%-?>xSUB?f{bc;zcdc@+mm}G}^j@hNiP>s>*zTi*;?PbOwwM525PK9Qdi0 zknr4EZvwk*gKb8|DI|Ap>##T8wF_J@_zlG=l7{4zNXdlh3}z~f<2(H>z^^A@_(#T( z@7GPc=KyiKnIarTlZi~!>Ft8gD;A!*m1-vhF0291&Mk>U+sc}~zt=gfsA zBkT_|ef;h^jb&^GYydS_k_snFWrv6?SGD-APSbGA$G0}5H3l>^9I9eR`^ZF}yZ0d) zeehVyg!#z8>j#h~^O3nF#9c^?kycY~L=(!06N;86JHmpmv5Y%idGO9d9CNh9S>@&+ zaFk89oZtSlM;-++cd>_m2Au6J7*%lWsRl3hDUDb^0kcwuqv%9A>si|upaW36% zl(;EeMzq&k2fEYGAZL~HCJGBXl8E_v?!4%RMszip#d7Z-axN>+`;c)1`T*)n!h3rA zp7kE$`Xb_iNYFK-^1Y>{UQK9p)U%AZ9WSOKNkxu#?4i$!q5tcOFLM|Z!4NL{a# zy@JaHFkIAI<}E0Pgi4;M8NoCLa{q)15+cvKWwn15Tvv0PUx0sbd0Y#`p0m8o(;cQ+ zv8s6Ira7RM{t)X^3K?yWCX^sTsztU`I}BNai!U2Pi2`r*cRSAgMk|i&F1*rE&k*U zvh4{G3XWCFr{f<3@Y32?HOl+N@S*jKS4IuvA{r0f7M))c9#;Ye%i(V+0P zTI_W}s}ht29IX!C)yEB%{H?{gTOBf$M|>?riSdBDSkWTWoawFH3K|J4Wn`Pb%d@J3 zTe|Fd6ZU!up{eRAJot@D^nz zs?0aD64xhk#IsM}NF}<@;nk)w_FPw*A^F+xi{vpj~CWDQSE`A@VEku!E+ALe3fcq|@uwnB_Y!?5T1yye!~+ zdVmM-Re##su-F=^UO_VI8dBW;P{>dSSOcYI{CA7Tz?^T=0wzGqI?x{^|2nfP_1_Vy z{?)Oec%TVtdf+KN((%EIb}{Zy=>`rJUQe**Oz0~SG>-Ou>F4cJ@gaVtJ&igr%n!f} zz6smKEuvHgvPK|pwQEfIysEtsZeR_@dv!usj+#jmHw;1S;40Tr+5;y3P6#mP^q#y( zuN$THr0g;q!WxV*ZQphgN6ayl4~-NNjF-V04CDeYM;Tk1aw8ev+KsaJYG$ux#rG_X!!Qz;D|&T6|1q zuWIA6A((GRhoMd5`c{fK>?OH+fKOI)U47Gwx1=V;=vtA9Jex;k5RgvLUO2J1!z@Ft z(r;Y?glncN#ZMc066w}wa5G34XWIPXVH}|WC`b&J<;~xFyq^4-mlujA;>_Ug42YcJ zCn>K9DkMHA1^ZH~{{t6IGO!>;B;G^J$*xa&=9%6@9G&pr1l`V*jy{~^!^nF8`tgC- zRoGVN5u}cPlCG{Gx~4aZ^=S}IRlptKGe3y9gHfdhKMsGvfFUtm)kG=V7`vX-KD2xs z(sJEibi^99&qZla0#B|RZORH8cd31B&W++1&z|Hll?C=JP(e@$cADIU%Y^harN{$j z(Hf%ZWIe_(QtXdh&QZU?d_-~IC^7$(QwbOXf&c_=?nH-hAYb@Mqb)2`U2T5;E<}z< zYkpiKXL0WP!JF?~=d+hnbo<2xTiO&yd+X_@kK;=tkPsSP8FAIs7zPY?RPdPA3+z6A zVqo?1MEm`U&J+5YqOGK&m(AJPi(5wnmcuP8PFfwX zH4KR??%L1kBwXOT6VDZN1_sx?r9_C9C1284cdyn4h3#D>y&xa=)bUXTHz9^OYfPep z0uD76d7uGsM1w;(^B`XznK#D^hyXbk!m{z;lVTLHCV)%bIReeBu%(kjP#}Ez!uTmq z#N64HMv3SF{#{CQioxOe6w)i9#`>wVCc#rtI^B3h;c5z|gnrbh5-{9V8bd5G2Q3^Q z(Tr%Wu(5TK*(hCg#!T#oKwUaxvZu6B`+?p8gSG*PHZD9*>ePnBom=`hhhJnHe=AK% zyM<=iYbKrU2|A-=(TcrSK$|!v zd?C7AvDj@(saqFQu-h z$zd{f+49M3hdN<%tO5C$M!QyQ4oThI+OQaeqrps-X^qG0*jVpisw~ETX6XC{2UoD> zj(-n9MjQ^HQQ3&W$wixxX@{k!I#*XmPr;b#0l=l>OfVKW1aL0!J$ry|$N_xDr}6@@ zw={Q8!Jh8I06P&j0DJG*1Rg-ZvN|#n*m9UL!GC!NaFpFcJh_JeK1oYM11$^ZfsU^E zmvuH7J1ye!tFKcb;oXmC-U&?L&(gChdZ_kfl!I&=}CPzlFil8nqSkCF75o=V?@~dBj z{2$T;Q#~%}G7YfMt|Eq%sap{o(0a1H|3jK-b*@LqAvk(GN2mYjLME|0(GQ3d! zBF=xE6I7p||Ed80B!obO0YC=+QT+ims+8K;BYHMcdJC9QKV;>9K%M?Uwf{SN3BsN4 z3j~CwOCaPZ3flizT`}|A{yYNBcp8lUs~-G6Kz)7sbI<-0ar=)A?Y}euh*2s9KWe&! zq_!OW&s{41PZ)mK z+F`RmBO5{dhnW+gUkyDtqA0*dOa-nswnqSWELKnd@(~mYq{-4pt;?8H*;^&o2w-`Q z2cz)f4-~LmA0e%g zcADS(g(sfFZmy3!8H-t4 z!btMtnTLmjLC((FK+(v7M1w)@wFn7=l97u834^$$p0Uv@5-p@B48krBqKXcB4n`zA zJPe|*#igwcjimML%}F?zfNT_gzLK%Fv(&R7;b8vpQprxw%HBrL&dADvgoEYb6(1jv zmxwj+n}-K@Qc_|fVP;`^sENXlpUe+G*?;-@@GS|$8(DobBZFVR7q?^tK44}2{ey>( zezxgHReo&|gVH-2Ba+{%4dgB)O~UY6&(7S)3hBu|Bzvgr|9!Gwo5f(DN5b@{#1GB- z=ZS#?jPxwb%zsM6_?JY#HWR3(qNBdUkGhKheF^+lFtWFHv@`Yxq zG??g_I5>dJ7)c(U*dHoG$4t+{#?0|!)P#hrfsY>k&B4elq{B%E9v zoWL&;Qws+pI}!#F3t(_x8yNuO`b%YhN{q|LJzlj4q8{4=m`mdGDE3eCi=wD7jN*Or z`Vt>%%kOksZ8l#yIf*Akp^>-nDYJMejhqg0@bWH3Kbdkptm#TE*e=+wzrMa1r@UQX zOy$$_ndG>?JMG(Oale`@ya3&fPap8l-7oL2Gu->#hu?c#BCyuC-8bK?NmQVP>5i3< zxCY>SDC4-ee1)i@=4^j)8f0{+!J-PSchYc4cze0L6~lkegJvKUp*dorC}b~%rT^h+ z>;2pJM2EIaaBYwwDn5?wq}y<-W``oH)7=$<2qWg7PS>;^%@!FCfG0Lp< z^2?j_v7HjRn!}J2sqSPA!ysT9u}D?}yEBI)=yB&6HehzA8#M;_6avprO z6=QsB%uxf%?AQ;Pm0tJGI9Ys+=gUEF0c|aD^WK!hwkDn;|B9qo<+V2a1Q~S4+wTWh=;WC+c&2Y-qDxM;IW`3+S%J6R=(vX=tOt z`1hv@yoe>AsP57GD_Cg(5u#wkSR!+uM*zs%Z18v z0h|UL3YlRQN^)opax(q4@1P0s6R2Csa?l*6(58ea6CNsXUv*+Y#g3*uc1+ z)H|>Pt$Z|Ga2Rr9w#rufIj6#|Z%5Sp^aw$W zLaOUTm>ja6F*clw@I5FW@;>S7xrmi#>W#O;Jd|hN@@;OMw)rDc>eUL{wPZOQ$V_}b zI=REn($#$94ERzTpYtLmIAlEjutX02PEu&}Wrs$64U3~(a@O3s2&vnzoxX8C3UhHO zZaRvyX&Q%6C4zQ)Uad=wqZkXbzSAMBodwVvJ$Q^Gh8@R)rf=r#G7~Ez^ZUmZi<~EX$Rx&7p46*+1&! zjC^r}S-Mdls0rxqmRg`kyk)qr*ATnw=YR@`$!PpuC#^g{Er3~#sEN|}e0N55#D4uCV=rKLfY)M}fr-t9|nS&8q~Pbqt6 zXE^&6Kj1{Mr|FY&Vl5?a_;VvKaa2KSem-we9+>a;JMlSM%10Qu3z_+#Y^nEwK#p0r z7AvViE#VL&)Nfb!OZ{rva&A;>l5@ z$(^Ow8<6p%Xj~z>o?6MU+%z#y{F__dMNZmmGJD8_j@l4LU3`@>vXL}JkYywk$u-fY zf+ep7U2!ml)$w(UYlZZ7o7=awm4=a!^>^_hzolWgcVg*QMbjRw3)kgQ* zJ;m6}`wp*Bw~wyt)?ji)LOPzqi}(qF(3b#$mJeff^pOkYFdUFis?9aJ%(aHOE(dU^%GX+#3WujgM(rNRL>{C&arA>OT zn$aHemKBS8t?A%rV7~R*6|x9Co{j|@z-GaLPaBex`by7kzJV;Jam8-W4RlJMfi7qa zmcP}Q=VQOEHW)s>u1Io|3@+_FQ06e?+$fWIN*U;!Kt=UYjHnUCA06H)&(-8qx2bb; z5pm2<;lP-63Pecr-S0!P9b)d|7j4is5qAmMDmORukB75qZisY6Z%XcZu|r9U7$Ynu zZyL}ztk0gVpyi*fu{EoMkP>jXi@mbIB45kEc#Ol026gACH!qdHI5Jxih?wqjb5A&v z=^Az=R#sW4ZZ)t*hT`d&E@JGyS)}0fd|cA|Rge@^G5Y!AKxDda<$70Cvt&qxX8r(; zZB;W38|QK`S1M3EJt;XXt^T2Qz}m-xf$RG9a&4mM_sr1a@}u>(y9V8+zO>z*zJ!D~ zk54#DDAZ4qQVPR8JWY1ZKdon^uvmILjU4ph{$6EsgU<67=3ilibp)>SyhcBBWSpXgNiZEgQhTw_)F{M*T(4`1!wo&2EiO)6R3PyDV>L&=lNHcl@#lx1-4R)MxAq ze6#05B}vN5LA`S?n)yu|bC&^v1UP}&Q4yvC@_5Eg21HE)Dc!z zWC#^n8ennNq)s>oE&`N|B|iNu#}90O?fJF*;#jNnGynnX|sE6Psf@mM(&#Tx2*k==PH>j$k9gOIPkGQDrjYtzr_ zj17J>SwMm87uhT+lmYoAK0qYQ$%pZasfFd+uXN;FHp2BdK+9AyqTbrjAG_`)1)ln- zYFfkPR8a-v6SNJ)kH2WGU!f?u{N$3ixR)T)^Q2t@sUN3>G_w0p-m6A#4wH#5uP?fTjOP=<$8{5PWnpb>T?)H*{vsuldy`=d1#jfxoQUhM^F~ch+tGU-WG->2t z6HAW+kd&;;BgiCos?L_ClD0z(uq+{?d@Atutr58&armgP=%DFoIkGdlpvG^hFUb|x z5!$EDwZjEf)YMll;OIX=P8&#M0LCh)XHNIcX^Ju~!AUN`w@{3FYM*WsbSd0V^i7fw zXGrkRAy5Z89nlC*J|j|XzOi1M(PPp#UqF6Vj9a4_zPaj@xaZ+JzEBj1w-#nxV$09s z!$tZL@`A-LS95s=nMU=(PLkpA$myH2@5tp?f&8joXd5exx7Wi(0fu?ZRw7px5rid( z;rnO81EtL+#YuO~`k=Hsb2^{GIGKVTg63d>`poSe^#S2lPgB-bRtH7XNI3pdHu}lR z=f+TooDpIEg)9@?cw^|>lw1(l-^@H97}+@?p%XPOHT}(STv!W(yXeP;5oM&^8l$yU zydVuiu8BiaeEN;;Y>>^-yU-VM^X^mjN#N1HcY1tX#e$J3;K(A(GF_0Zi56go5De0U zejFNb3bJ%^J(D$8pt~+jF*Y0B&Y-mwEW~Rk#Gd->gVVQ8F*XbtKR&fiTlSJsmnUn| zykfjixt?MNvZ{VuP4o!P6qZ4#n>8p(<(VCKHVGM`4CB=(HKRUH#$;i|c>ZL0^Vp}! zZS33t#{r?^wxdXd$KRsy1{Gk<^~qbkjuHw=2W4ssOpUh43SDzytId|4-_V9Slh!Ut zCF*lASCc)KWfTjmI-wC*dE`uU{XT&!@2xp(TTgOrFO<1YQV(7vJliJ*`>i3{Sma>O z+0y>ayeC^xcgJNZ@=KOEUe6HbC%udS zIwJ+S?(7HEYOl2kGV7ES;)cUn#y^v*m$HMg8-8YT~ z-c3FOJh#DJ60J8M68nsb&0oXSnWbuogu=4gS-y2;6WY1C!5TA!{el9Af9^lhSw2-k z>!aC0i3HtFc3n%)Fu(mN1nK6ijDavTZqa)Ghg>`)|yZp6qD~WgAV4tsV?)SQC&{4mSIv+qjw6l z=DY*UB%+>T1DNV;JF5jnr)EB>Qf8yw`V2#H?BoG`3|z%e#gQMY%WzXEzEOuTfRL0W zkBoyfXy7XumdX|BufTZ9YDenrd0zkcRvN}NT~P^KP$rfAtu;0W7k>UuWF`?3+!jyq z=|Oswa_ZT+o8AkSOq^$B8r8;$j|SUb6Ud1I;{9W1>mmS~#+PVBWfYMm+f<)n3<5y=uYYjCby?PrJT5u zvOLo$w@s?{<=65&dRW z;q}b!X+izhZ(&p@7P`J%!59zPn2YRe-{ZU9zICYI zTyZ#O({vEU)ZWJyR?eNh8A!g}YqgExYHG@Za5e2h5(@2>q+X>H)Z|31P{cjP=Kl!Y zlQprrm+$QvLW;$L$^SmoqA`V!_Pra)B+9`PlDwh1pv_JkfrOPTS;#}D=&p&A2`YHX z<-~!*J=U4rWleeb<^neSOJ(84+00L(#VG|<9D3eMpx3f=92_~kqHF16+f{Q%hQej{ z+>dP^6-(ZzoXYW;?uOPw z1_DsVMz&$l@$Qi_*H^sAJ6iKGGH~xHOOEW|8Yj8250gtBP4*VtpI{YO?CW^6wsOMw z7F1@{qD+{CIK`~CX}&X}K02SUqPDNT$t#M0oAWPZ7F| zqmTA%@8g`Mm8y>_)5NQx_c0o;XIYw!V>KxS2%3e}YUdxJ-ld3ujM=WH7?)mSoWex9 zZxA@)%H3>-w`ZhD5eTa$T|Ni1J94o}D2ThPw{xRYOtKzYMZ6`NbPTT_?&WJUaCJCG zN%nYKkoNXG@$v@en=uhN`ORRf|L%#cKOf$NsAI}oxtewAJ`Ri`hQ$e&fshrW@W;>F z)JsMNI9RZvvy7-I*OJwwvKZ{YrHJ}E80krk^m4?wQCUS;h%rBPn~bMnl%5i>X_W0P zbX?-pvXyM5#B^qP6bmkfnk2F#H&a-P^N;lO?j`XZzd*&WFpvcVwuSOQjb>W$8p*MDf7x@;gQ$e=S`c9o?P>F8jEYK9qX z$^2xFi|3;0NpiGufXfjzdOt4}U0)tmVu&bCb*Tr`YFC1xU8A$PWdr-f&ty_tm z3A*3?jD^YHjIE|y;Dk?+NA^o_ZyFUzEC#j}sV z0AVfjycQGYgvO_Ck5D`{ko_VWb3Hz$>L$%8Psz3nvr`Dq?WZCZ@2FJzIH;jPwh-Q! zIx8|M|DzJdu>LcT#DRUDXIV4|_O2q*oLqsKY|DNuU#nP*F;nS?Biq~(8XZEO8cuQ7 z8gHO+zO}A;x0E6p()A%D4>UGR#skSfSd)#(t6f1lSf}7%H+uZusFsnhi=5N^0=uUN z?l21>Djn6uHkDV|*M%ST)y2stAx#yw;N9z@_X)!rG~qoHIbsZu*+ik@rEkxWUid_k#D~d1%HHx^7MQd>e zC^(#RDbqA!PLvXI_Apf_EWf8~K!uLNgO7nRyLD0HC|b8&k7ZES_>1IMKEgcZu#VuHO( zeh+#rhFKxCymbG41-hMwryGLa{SY1E2I#GK(``I`_hyx&S0d~niV9g^DO%))e{{^E zr*)n;o%WWRZpbW=Q=@hzZVaUiQZsmqQie>H_oX0;Z5Df?6bX1C`9i5CNrlcEF8|s?;YZVG4?U3ZRmUfBLuG`DdoQcbNCha+VeQ@8mUPeX`K5R}UZIfhLiS#Gd z{ucJUSZgEIh?V>5=R?94noW|VDX+!jgXR=JDf?89$BaQ8$d1npdAUq8HCw=~3x;fY z2hT-o%9UR*`vn+oezkn^jzB=u+TA_ysCS35;--|nG$5|(U8SqBgDVpK;&Z7~$ijuJ z%3A4KzGZSg{ujXyv)_{z@XrCi-;_{3|&?wPa1@r?eFX!lAa0+9XEdG%-4^{ja0@NKR&skc8yl6 zaV=kfsX-2r4~4j|oiD*F7H2Jha$(f+?&!es+3IC;7;atkOl-u_9#*1PZF)0CM!Fm6 z2I&wGk(TZT>5xtV>28n)0lybKp6fY>`+fKO{k;F^X1{yyXJ+;@Ypt1i*6e&J=8I2n z8r!~ZqnA)`LG7U-Ccd{~R#bIA;Q5Bk!1EdhwDYYjuVi>*Zg&>j)qDYS=`j_V2g+GQ zcvEp~1m7#%9Awp3q%_N)hSRE-W7UeERU|`!OhibkjqF!trck>-sb2MC?%EPKN1liX zw>aX-L5#{U(Q=fchT7`_;7EB~%_s8QnJk@0DkldXUu~%x2wRAf-7A>IYX-WiB z2JonHM*5bEfrtP=WW`v%(kgFcV431_n9uoBNA!N^g}DodB}Cqk1Wlje^;A+{J)xfP zd$%o?V!!X17`8F{G|ne0o+$jQCQ&a#Q@fzq;bkC{=XYdylf1Wf>s=X*QCVs7`$JqK z9%ckBvyq-^tJ<_Ib8&guX1DrL^-}$H5CFK3U$0WFER)P@0~Ddv=aqvSXVG9hg0&+pnR4s>#k~;iz8w%y~quXwdju zy3;uUCjjNToOvVvk&&Nr1y;Yf$|TM*L01@!SaSlHgQRl*2@NUg0(3 z9k`1ov;L8lVK5@Gz%xSs^4)~R@bxiM-Ry&CR+W|mL)cQ)GX+ar?qdAj`C9hU1I*S1 zI2eD;21v_neCFBC1axDN4kuAHD>fk;wXZa`bWkO3Zy^$voZt?EnJ;&Yd@-I;DV4V* z>KVN)vMw#)CYmZtT2uLc{t5mD&0+OscPzB^jOa3@gv>;*o}*rn%t8qbfpE(=acgWY zA`C0y3tQsnxulSz7DD)w{*be(;wTAF%2TafQDXiSW)}@bKmMC_0>4ja3!aHUA;2-H%-@Zya1XlsMsGqyu4%CkH`8^ZN6w(H=kz5BR!^&*K7Btt z3|U=r9Z`M;%Ef+~$<6cj_O5u%l%d>Ml8vA<6p2c2icWN^RFR2Urs{`9Q9iqW zM_cgmt*zBA65ukL=d?dV-a&@R&C#n=(tOD(R^o9>;tYF?#M8k|HmwmQNB=?3-~&o= ztlmO5HIL~e*0g5KR_=^ST)rd%oo~g80=1(&)u%Qihp;IU#=Y#608kw2E9$->B^il| zqLuJKG7dq~KATFgiBN~()^COGUy8kqv_a@m%ZqL%tpwlJNQ+wr z3zX1c*$*#bAOtN?p7uvGp(~`~Fxs;32Zpr$ruaPXjjskKp!<){#nOx4KzPBb zQg0eBh4#Cpuwz=UPPnzMHyNL*o|ihY+cbNBg>fvpJ-Q6~#9IB@Qx{AnN?dt7c4tuN zV<=w~;yn0Lp&%Ao$8CkXQ8xGMJ> zGA>NKSHzbmr3W*E{TH(DJaLXz`UHYlX*#x^#6c+KN_Iu8s_9%{xr+LTJ!Kr;J|Deg zhq$xttj%!{KW%^unU07tc%|8G4|mx7ffF?*WB9|syHN0Hb)h2#>dWtbVt&AZ*YaQ( zX(aT7II)a#3FLAWExFJ%JIT>N#>)8n_UuR~&d>lzD$J5DaT4M^Y83mWg(O4Db{XRL zM{S`qb0KAwVoUI`0!bvGdn70*70^^({KV};L~Yzgv;t`3#BD>w$kaOg#F}kPVoT>_ zfcY=!VnWm64XxT^<^a0;2O@R!%`}CS#fj0gECCoKhzl?rxmd-e z69-o-tqq}5S{iqltuWXjoff|hL5G%DQAo(FCmf*yc2J$^3pwYsxY`!bbl1&!Zj4LR>r-BX!@=!H)Vhu3S0c3LoV z-FbOT68EXpn~?F#HR9>JPRGkso|X7nl9xi%4l!q3mBO8O2NeK(mWizQtXUC=vWmW{ zW%d-|raQ-0il>ubW-W%V@T6eST#?^W5e!Ou(WZx4zx|dZnz(ei0qa%nf`I!Dx|>!a zqIq1gy?%TRYlRfa=FDfU!^iIVBFt-rDsbMw&RWl;R_^GXU>6AbCIa9PmJSY8IM+19 z0XWVhNeeM0>HwR{CGyA9Zb>rcZM{hCvO+wA(74 zd0D|WbyGPJrTL0hnfbPEe1rN~oo5R&p?{Ung;(gSkpPi%H*)zO9#MFkV< z6XfEg%Wq&0d)EqHF3sZ)auw|2I#mxAzi@(v)n+ijW9vgD6v^t4A(s70qx>0^O{D#m zo)pYAO;{l&0$Qxl$0rBAO1w=}KJv4n<@wRLm8Qix>3JgJY|z76L833zrI8QM$+zS! z^302^N~+ChOwFC9$;actM9X{9V#qBKGOu1;Q<9H_!h zs$+cR?Fexwab|LVVIi*K)@Cdv{-lE%_uJ~^_1&qe>5msQUton2OW)2vv-!T(VbcH9 zmqVP6PO(|nn>O>^r}Xe$&K;9&>Dhk78eCPCGljK;O{yrxzGpVN?p{Z5*h!1D+`gY) zU%VX7u)m7z?@&to{BogG_f=JP#W(AQt3dOM-HUSE+)Q6hn|aG`Q=h8kfNb!$ z!ElfM0yH-QVdG^QD0>~7ww zAoLOt$lzYoP&DthF6dMCXTjrqNsCex|8`1yIKU%C^5G3_Y)~1QUoSR%ol&ql1YRJx zv(dbXHFH z@)%=q#?l2^I+ui0@W+=(DYHn>(rO#TyW%L=Dw6~(6Cd6WaS;uQTg61=R7RtV(owb=uSJB&Yc&p(0--p(e%P$!Rv(o z@C%Cts?mCgipk9}cX{EPfff3Z(^3=S%FBTrl!z?fNIa1jAHA;EzCSsg!0did9Q~BX z{1hLtF#l5t#BXhFuc%JwP)8en2y^|Ac;wW-Pxb27d?5 zSm=SZ1wykh1G)b%=*MUV>;u2G!C6><(7*;~VFyD0tHB?m8Su*A3eCs_AY=m8hY`R^ z$OQP=uUQ_W8Q?e5j1dUS#Kc0#$i_~{#LW1|y9PW)vj-{RZ)EsGtr?hq)S8juC-h@9 z19rdP+TaZAKyw1D4Z&slCl7R{M|AYDxXu|=I(F{n& z-w%D?ukWG%v}S|JRY&h$0rWq4EGqAQmhsg{8LbLrrGuFpw_8{5)jkRV3w)ee31KLw&=AQ&- zeT-$SznNtWKi;2w%_bC zA8O48WY|Nkf7(%CUw;JpH?!;k8pxjqXf~EVSjP4k%h-OiF?)ap%E%v}8GZ&SY>&|l z=wE)TF}sImq6hXXV1qL;{PdY@kI@Y1(0@BL1N(h=24pxr;Ex7pe~f0pX!*B8Gv3EW zz@Y^r^Uux&>}~&~8T)^OX22wZ2k1Y&2hhSj0{xq5#t7`fOhBjg(69fD6xbi58T)^M zX7~9N48UlNf$b;FfG__awFc1tZkpXQ8)(NF@0rf@N52NpKSDDA{eOdIj6hd?Pcz0p zf)oI74E9K^{~I*BAM@Npv;5Jy0Q8U03_$;zY4%`BfR5^+!P)+VeuQQKpriTi{h9%A zKe7kX>}NmHIgC6EV8H%cY4!kpKjwL`W`6|3 z0LI5?#`xc`W%XNpLF0$+j~2scPy4=b$nz=`3~rP6q7gAKPqcLVqJysEsx)Hc`OaqV zEX`VAF!eC@`rFs5ochEoQk}lZv}2OjUbG2Y#>G)R0|y_N8wwyt^4cV5S$t&RV~Z_D z6e)<6SXv|36u|bzpG1~ew8l{-G{{Z1p1v%Lm4uJT=9j~dCHHj+2Pc%!yAbTCn;&Ge)`yRtcq>hPBgV%|LGC?+uptpqHmmIT8zJz%^$I z2C)tAL3HlqkFTrn?)@~p_RP|A6@%E`8rh%>_|(|vn!?k^ay^rQc?WXpJzYb2^1*)e^&RDfDQ#87qw$;PN0 zzmx6KU(<{j@%-S3rt)+Yez!?TpWh4hTj3i|<4V@@Mr=1EueMc>!UWU4md{-8-)p!v z+%yTGR=-+^hQVJHC~h*4eUTn5J;~5>+LmJyJ zy6Qd$EB2J}OT`}TijDJadW)@Gh9ra@eW85fOS)`FpT&wLsYSr}rdG{DaZ5=JgY{a% zpSm3otY5KCxUfMVu};FT&&&eWFYnh>u91~;EX#Fa)q-+J%jmw^YQHXEP=f_)<#uEe z_jaTgOV5=PD-V?0G$cv7KPj>5Y31&I>gj-_M>)LC3h00?$hwMlD}hW;a7Wgw>r>kj z-W(=Y0aUc!f|y3+lp-0coYM;ff-S#(vsQaXC}39@Gk zBN}Lid%W9#3WNA{s_mN*l5K|Lzyq_*V1bxEmUdi*PngelZnARbl}&IF#|9OthG#9( z*Py*G!d`JRuU-jq;@7;gHL^KpRG;lj>2sy=t%|KaZq+^sh#U|>cd|cYSiWVEeb;hJ zn}$+9X?_)-SUQ=hN0Y*HeRU~)d6Q3H#r#U|V{yus!S>YxH+%e+N&PD-%d_hq{pZ8l zftOc77gtS-&3i82!C+s9?>@RR_%Pl3S1%w3+*bi;kmY`Cjc^})0hf8`MD1;yZ14A1 z5L5*2RAB7<($H8zRDh5U_}9O@o1xpiWBTi^0Dri7IziyZ3c$S<90)c4@B<3>?o`^? z=%xOj8v(EZJu?gQk5S(J@+9zLc$goHm%uXoSW0|2xc4Iu2lw&9{lUYc*Tcbm$o6n> zA3!}E{Ji*KvE=^D!_vjW!Ox3EGe0(oxIg%L@xxT<{c|2> zF7FQ><`Ewbem)sE6ZhlE4>Mj5KmC02!)(y~nTNTVhl8INv;Qpf!z=>urypbbhl4+! z3;@n{Jv{lx==0&HKb{PD7@6Ilc^D@>9Q?faVGQwb=I6x^@%8n{2Xg&?Y|!y*Q6HIH{xxFx zcTv(qS^nBT{(jrPkCGmlRsQwn$D^c&`RTtR{)hW-{1MgC{f7NFfSmgO*?*%p<`DFh zphc7nmZ>SKi9O>L3nf0c#3y(G=5^rd&7ac zItHi3T1D|5Lopb09pY#)Vg!tb=uJFm8RlSG623fH@(8t>zwVdTH!=LDd{_OA2sI)V zW5lm!`vX~Mut+Roc!VS4PJKx^mt2P;9UgGzC>kYPYKAq z8z8Da);9}3ZvjD^9MGXNd3o7`FE2Y-;&slKtMA#JR+l$jx1Js>Yce$pj+DqT?hrD&4SC6tq^66-}h4*$g z^;&*ab}a@Mh4f7N`bpU}$MnUKd;Caj`%JX;D+yhQbWpyC zFwlTig}o?YP2??VgRM(O=>`MY@Mkt{s>QO2o;hfkI(`2%4IVdvrI-O0Tkau_rzoOI7Vpy-tefzF!%g7IncZ4ndY7yn4RXa(!b->7h;f)72=4En&Vt?%i(a~2{<{dK*y9$^WV*nd3WNt z)6{>Q7%>nb2rVr9+_i7s{evXI^dP^^g-;iU7>uAm`VjGOHvMb%h>w>RH=~pzK`0s8 zYMC0R`5J}(XVaA>EqC>6FH=lBw1i|mZ|~*{4-XU?Oe1%}#S1H>zBgK*vmt&LDq#T+ zFzYOBf5B?gEK<0&@mY0u1+qv?Ag%{)uaBvvkoPRJ`|~$ES#l&H7WQsk_t&+ysY?E( z;#yi*ZAE1!YUOrqegVj`Wd{XTkV(n|RK|sqx_-r7IiQLl5t$l>qSG2$c1Z^<9Xkq2 z3xY?>DqJzKBq(3#@oQUYyK^LLyFUD^&E>Xk!g|U zOnt;;{Txms+_SwYSMiXzi^wkOak&C zbp$-yZH#Q!b&En8oUky0qA;(nu2I4wX{Ro`uvyTA5t++6cv~a~xz2_bwT*Fo+-@^# z$e7s2GsV*0EtZ}^q-&|lu>3549WU9bQOWDpw+`_^5cyv>F4-kk0nrgH zuGQ{56+TERsnr9}b@C=!{5~g=YS}mh;~}IcqavxbWtj>ssP?L|g7g~Estp__FO~qt zYEs{P*_mHOd*jww-!Xmg+iOJhMv97k^)AY=kAINBq1{?)XmC5e+8s+*)%1pFIgd!w zx-E)RoL-8rmE(oeT;V?5o~6ykw|Y$@@A-Xj}U@iF8Kkv>OvBapQ=Lv_F1hN>TB}4O8^qaog(fl$>1srq% z2(_TyikmJYn6EBarD>c_l-!Lf-molU7L11i(*RRWNYjv^3~M+ z)XBn-gyZTv;D$f?Bo63!#rcYLs-gWh4e!K!Qs5H^Kx9w(j+->|Y;&Ph(4gS1F znZiT?l#=0X_){|V5;DkL#bYDG_AwO_TI zX87rY?8Y+MQjTlL<`mIGB|yzd<^-@&N)|S)=F6$4Y&9hJ#)_j|e3focuk_SsNWn@Z zS%66d;8wc4L_=L}S}*kL_T$j>Nm|n^E0bIBfoqxn=&=;oHEm;VszJ5bDG;xjo2D?? zU5|m%e{0le2i)!^g$jy~jd_mVaP0Lln1#GUlgp~MQ?jT}PhT+3x}W5HTCdF1bo}k) z$4x)NOg|g@3Ssik9|yn6y$w2dP0svgFWF9BZGhirPmc?gU|NwIN2gPQ{k>EsAm5r) z{BEU|)3pep)6DL0HM>?pa4aJtU$y`Z9E07tE}W@^IZ@rvD-u4usR-xI#iuKOTT1o0 zItuy&wN}0=hgj_AO?e7(q^lsC&jem(V*JX z+$dgnoA4T01$md8@_7Jy0?CrTQUkW8wDHhp91BoEA1T9IFrXVNwk+dy)Rrb^W^9ST z9R@phI!ZlcL1viDZddBa`zN|kW(3(!Fe^MD02Jjx9UC~$5rv-P)9vSu9yNt`&$SEr z@WSWGCw)pGQ-NWE=%Lyd+{Q;DXf~#uPLbf* z;bnH;uivX+ks9si6mHmo+j$~+O0SyQjYI@}e4U|1h(la#;lU-Co!~CqQlMa?Yae0d z7V%U924g1iizs_1jS^`cUlaA+cG@z^xt^M1(T!V?Fx|5OYyuh3#%n#}?_LmaYT?ZC z6&%6R(A_zuG2)TvMGnTtOjK-Xr)mD)7@wYC;~h3FsW1Q-BwOF^3JG+rf`ppq3oR{7 zn#rRkw2WF!o!kaaWTkcuN}UzvUwa87-WSUcq{Ws|@?qRs} z6wPqBS$}*z0^rh@wr_r*U>SU_r#|hJdr7A+g+*=F`9-V|Q_;V&A*ejCSs%vGh#Jt#DH9ovIg=o55 znsYB9Euk=K@E z0oxVE3GcM_!1}uS7={{0Q)%m6wol~+-&NDL(OPg5?PLWN&=_KOgqFO((o9vz&Z{eN z7}mQ+OWGM!?Wf|=t;u2=y(MuF1SRF4i6*0CDt8Y$=@tnw$wwi0z(Cc=b1TUV-|cAf{bD6~?QmU=9C#QVY!OgGvDwG_(_l|U3e`kV60%gh zaVUIdp>xZ!u6laqADLCZ?wFLFP9nBGI`=G_Fcp3_zmHrP&UKP%T6m3;{)#3Qhg3q< zro!o>RD`VS>|iM%Xw{-tvVF-_xS@{Jo1~(ZtQHju_$9sd(b5uT|-xKachxH#^4o0Y@r zD#NJzxQ0XHs8G_2T^EopGYIIYM#Sx6T;2pTq`a~?Vl*Y(0xF^y2rYai3Fi?Hu`}9H z8V6K9*}`5xqX=_I>fA+`NmcuFc51L5zV7(azk0%K)`KvsFC$}-?huTv&?o1UilCr6 z3k#VfXVZdAnC|Pwo%Vvt(l&kEs@*7{sf3NJmccfzSEE8z1smM>1_EV<&+_>air7U* zBP0}iEDv(`N=on%0g-$@xyKz-W))LG3^psfyD9a84t`diFXyWzXO3^KOc&|r^kg2X ztJa&G7EIl;`I-7sj)govpZxIazkE1NK|CjTy(nwg7uO;xSAT0a7*xz4lw3Wl5_1p1a&NL zS+R%){$ zIDQiW#|NKIIrY8$cz7M_=L47pWpA|u8*E?2(sXc<_%D?B_)(P$z8%Knc@Q?MWB(hShL1^kS2RqK}h=Si_?P@gv{PX9+}gnjlI|Sq&sv zW@xZFs-!8pP(Gs*e5bu%w5>#0Q$)9&ux}k~-^TIR$GeZmGic`3 zbQJ2}sb_9p2|_p*_)@y)s!V7+Yf>IOIjf#iaLUlbgttaEgDphn8P|TN!{K%&e0bjT zta+tYnr<1rgsr7|+2p#>Id#Fg&?^a0DQ00bt3>y#&UERN^t?XDyMa)586Q~-eREqn z42Gs<+L(w1quamR(h?`mH}>U0mt!suP96dg$Hx+_+(t)fkIgzvdM7UYVcZ4PR^tAW zNCLdpkf5m)q$s^xZN)b3$}(>%+lZs|*qSAEYiXMu79|)Yx|*-INj@wpGa9NEQi70O zN@dD&58YVRZMM=48a3UqowP%m45uX*1=BWI+&EHd)EIh`xz-2AF$ zKo#|vpRuDFGQTIKRt1^*B;TjLO1*x}KiFIFQlU3N9 zsBIlua&>)R=&!(1n5&=;smc*D83oi7LqYLHa|+9=l@eW*QaD|>oa?u(u9|%1tMd>r z!VmTd3*lgO2@yLY_byy;@r3?5$*Wctl|E8wQ-e~8=e(;t4SN11+6v;3(t17;}v zxtjDllDO{Em>#rNz}25$pdZs)0YyT;6`Gla8K??pxZl?ts6hHdkPZOq{2mt6eqEUY z05yld6`JK95}35d0u%=`G5yrm0W-TEfxce^{Oe-V|0=uYe*1eyV8$mi5T23!r*IXR z1o2O3pmzMXLNfppX&CR5Gl4oTrk@FW0OrSN22|huR_KT98emEbGcd1+f&Hhf@3CxH z!0*n6Wdt&t5s1VDl*#@n!w3BO5rRKE*I&d}KfXZlzk1hY4|rIM{yX&V&W2?MuH!QTlX36!$C&7U_Up&8 zVF4_^nPx1&Bpybf=HPyLpYdmU4FFh{e|E0lo(=l|P5%S>PYEZ`#Qg&rC<*xGW&aO! zlt0uJf0G3K$QS#IB;bem;+|%|e6!;}(oz0mjece6BRO}!uGarMg}_76{%S)1e%rrS z2s~8c-{1VWLV%I!fo?x<`}xTX2Ect&{J+1Lk(1nSoyvz6bRpK%K|L0jAgCeu@qGYh4xZx#A@K2- zRVJ$O7$*`3RH$&;vbA>5FW)E{$*hmt*y6nOFuazCu^7S_x+M(McV(#EQ~@g_wLyrj z%gv}{^{hpr*lC0iuv!t-rY%~T2z^+6VeTYdvEllBG-??xJJlxLx|Pr+AbCglFInHj z#7LUdy>(xNuz|VYTn;pXH8$E)^yMz)Yk)=x#A5z3IkQ$G)0RYaMFP)iwCH;1Pm)L; zZX8rme*ylKD^t!@gf*?DT8IEp-8muJws1+n602zf`udFke-Y_xdqU*?Grnc-CYbeT zU#9iw5doGW3@T+o1K6%I09J^ZQ71AYr=kye#GC6fCJ6HLK4b?Q7&s3DVi+1bFNJ!B za@?zG@h+s!Yz`P-T^fw0WTnLxj2TPp;FO$&z$AE9D@^P|8)hQpfhBo_CwA*8%v@ zO^4R)wvk`cdM;NKezMVlt(m$@EFvrMR3G?>i`hFz?H!~A>7MJ>kY{w$e2|^WwOK<0 z=VJ3z(@@%MnStYdRsGx|Cr`)uZUF6(jf&A@7}Ri4UDG73TGvTqwQ~9qFvZmwa2ey1c%i{N90^ zy9V_rsiq`-cZcA~#y6!$W8{a#*`NLIhb#Tfs<}VJS$|Q@{ctk>p=u8B5XJ+&A>bjX zemM9k>v{+{AI|&%19%9=?$11gMh^!+FMbFc9?tx{_@~zHhl9KS>A~Fq5B?z!`_=pY z4}8H7&D`I7!H>+!zxaZOl+M3v>i@6bZvuYZ=|1n}Ar6jeHuL$(+ z1bJmJ#Bv-dKA?evFh0}ud#lCuw2^NCvZn{%^jq2tUKFycw^zP-Os0_YyMhCrAa1(( zcbaHdF~SN8%5T4W+zssA-Q3Kub-uhP-5h>(vA$cn`0D2TfR2E7{^R0d%XfzF-)|1b zFOIUb?=A+DL|@f}lxKyDf^!=|^K{O8-RT8ytu}q(^~MmrTz_|HhwFKHbTi}f zoj)$pS)=M;V*~XpHomWpmhk&qB*fzgydoAt<72_J{WRg{CO6UT@uI=Z2yxOP{$^`B zF;nU<_D45Vg{dgTxRt3A#8;UE*hSVh)A^N?;~hida2e=q(s@%M-|6uMed=)oeHNe{ z0Ajr`?=!tPKetBMcy(}9`D}x=G1dFH%)`M!p?+oS`fYieBNM=SWKi}cX5LlCyhg&D z5#%$n;Y;Ezlom_O;I*dBh~_dJCrakY!)K#Hl+{ATO6C=M;sXrV!L%p@S33f=9$RrIu#a{UGrj6$}4?*-)|0R{u-Nlu8#1s0ov_~GOer0 zVLYWZkZg+KAsm^XRWgUhEa&PeHtTvr1-7KpZ!G5!;BdOl#`#l|aNG_5$<=SWVB4);4Mflq(j;_dV=48)eL0bVjWia2C$lz)5QV?5WbCK6Om>i8; zY9L1s_uQPM+JJcS*)pC4EY)IPg82a;f|sJ8{KTS`5cn~zkZ+#)WoVHTp6b+`>Ud)g zzH7P6O7pEX^%&EW$ZngrGiXPWKJNc~JlezDiCpm7#UDNplc!5|(q7@VmOa*IGWAQ^ zo=&s7_Fg5@6?A0Kq7h``V$-#n!BZ%ox_GI$Ev`=q^6z*j^{0?*Ce3Va#gATlf{^>Y z19S~S1#%1MY}$2fyJfM-BfdXW4UXoxkqM_lzsc^qxy2|XL)>QAFzPPF5R7Z{CjV?1 zi$kr|dm>+7N#wBqqTU%lm$^pgQ%pR9@96)+us;3S6j+y_4&6j?Q7)zB~xi zOo?Rh!Q`hM`j^Ylpm4ymj5Aq03Jocsl#! zxdKqSy$h8SWshY^31{Qo3cKV70o3J|R6NLGQGvt@WDamkE*ei*3vNHA4A#D6$Ri>l z-xkFdaqhdex=qAkWQrc}8Z!3c{hG)e#T3MT82ak+tIBSud79L#mH3Fj*xNfVX-{!L zqlU@g4IS_z^~kb{#81@AO?AfIY*fOQV2Gc2N3u<`WnEW6Rcu-7Yv$r5~Y z``BZ`EG6G((-tGmYg1_TbQo_N-0jV`7jK;c&p{5g>jiY1=bZaG+$o&2r!v#Idp)jH z_x-#*u2>9lF;^N8wmw!_n5@4j3GVOQ$XPTB(rd3OA`@jIfyEb<(pF55MxUeSe3nv8 zVW4ST&s+!2Kf3n>&9bnlBG|Pwzd>b!OK`zQa-m(ot;QrKa9X7L1t&jM(#3EgFzw>73|HfQgHN#xYywA(kl7L1CLM^RI>)J0$_?e7+ zeqp_W=fyy3%i5se9YXxJ^C8dN{MW$G^yukdY(6nS6~M{w2x81kPL;|SWuD~PpH#S6 z3Ws^^R-qMIgT0I-I!hJ(qHk9>4q-f$#|7T~QVcTe1*}w3#&ao2@H+YcdUp8C7!TnT zHcD;e2q{!61YvYxeHOI-&w3GUV5}&KL2ha7L_`QDxKxgw{*KT-p6v_Jxn3$Qn=t&( zj?X&t1O}pP%Oj)LmJE{fJQto)1w?;7wsmTAf%I{JNL24@bQmfB?5nuZsWb{Ta-*&g z#f@N=b&eYjMy)=EfNo#^nf}diCJV>_N_zzpEJT3vN|;&O8qM`Lka2j2Pn1#6aR*c? zSZKIZL&*5?JSr1OfOG2nQ|Q-$&(JQKIg?0e{0LrypL1bm5#kp#yy1#|LbW)W7l@vU zPD+Wmm`cyf$NnjUjGkyh-dXJBYh`Ls>ALgjRUd%)a5?*UigUi~#uo^3lSxU4-WHl% zl%O>d=`O{^nS&IOO zYbxpyPq4EJlP6PlfKFX+rYE}xZNjT5CQq(yZ5v-olm*?t=EDeQpWV#C_uHAK9>0d4Ki0~w}1A>HbEF8H(;!hqC{G|uH|pDe@@9;iX@FuzF86gmz#{(v7@TlNa*a^6Cez}ZmMf_)@@ zpT2CxN}7ce6m5{Ps*AC|9-TSTQo-w~MqO|JrCa(hGpz8St{|QZC8I2GJqPG+o6JyB z&#VxnpGj{P!~3P4_zn)>fCmg&BQ*?ARFZ5c0NC~MGoLRy3g!?IY^`(dY^4bJY|3=H z`tyO6a{)_g`$}gA*#mb(dQY#IAgbP&24=SN+f^XpVWh|-L zy*|n~EzfMJDmUfNk+r36%BVX$e&HK)YD-dvxJ-wP=n&{u(gmi*a5Yh4ukQ0=1hzXB z96JO8To|=gu%-w9X_b7FxNX_STazi$Cz}AsVJ_t-Iu^)+eqqtCOQ~w0xiTOW{Up1f z+ZCJ10(Y9Nn)k8!V};u)S27A&VDL+lUNT#7Lb!4=>lKjBqo3V7 zcfKmoXLBy?<;+vmYcO*#YcS)m*+&xzS;s^<|{v2a=@xAc+f%Ax|HyC7pf%V$ld*i#-(r)FCp07->-m8k|nK zpq@<30JJrF9@Mb-BIS-GjwS-yEQb!nVOR(4Av`8GqNuS@H#(2)FXo3o|*QB`%I02?EfjR^lK>U?*d|fS5StHE~=p{?<8B9S<5#P(OTL>qdKumeB6qb z-On`26K!My8m2n2i{PbN1n*MHA>IAI@6%yhSVk(YX3V%Wb4^Wi#L(B=HA)m=ak`Qx zNf+_Qz3sO1kHQaS*~@n=(*N#Itmbb`vGL69NDXI|J`mp6L^z*636f$diROJ#b+P^k z^<32Ki^C^D9FU1s7qckc2k%)hX7N#@eD_MUi0nENLvkZILJMB!UKDtEe+$+&=w(%D zTwl;l?h56!_nLoQfTQZfYQbn3WnOjiKJRY$;*{AG^F!X9I;zuNIcAisxo2kIB`Wo) z9zldTGkGnAcdqHn%*fOD@nXiA@b9A&3IwTFiw*#|KIPN{H=X)EQv>kY^;A4JOyu4} z3|QXL-Z`YH0)ZXo0)c`ZQ;3>3{H1KUSutL&a#fZEcNeLwmtrX1CcA^| z2&Dka<2p4v{I9Oo!Fmic?rD5O#@1se9<;z1ORINo#ZH;X30s*+`s{@3z~|xyCEW1C zo{hwiTIQ2+;Qr^Fv#@5~jI+m`$Gf$rf#6v66eOLRQK!*jwnXbeQ-Y#QvmO0INQPe! zOr(N0G&;=obYg0Wu$i?d#F?$oa?Zq_P+VbkdA$!OH(GJUup|2BxFw!Aok%!TSOc+y zf5O)Tazh)6fIEPcasx$6FwSn0T!0TdMFulg&=1^yw}w@aL-bJ73FVSMm?fN`-1iL89iy!YYkx_~^8F2@g4rB-Lbsa_WN0uNF;_KG zuOt;t_!W8=(^>`?U3AEO9FS6gosnL5gCRUd!N-qfI=sgppsL&%@(gXl;euH>!A)dU z=D6@u#8vKEZI*a{A4}eCd@F%AdZ^Jv*f@=Qi{Vur3Ug=MI#zz#7&O9aHKEnO=S?ef z>taL67o^*8`lDa;fmSNt%YXe=`cR#;OY!MOqq`&dE#+!Sgr@C#Ou0I@heB+D=pyzncOE~aeq(FtDg z&xAXzx%Lh0iiv8~cKvvwmJp&)rGOA*c5*TLOQD=NcCjqEv7$OLHiZU_a*>p|xJ-pS z2I}Sv*Q0{TxY2W4lDU52_xuQOPXC9tFM+#i`uaDQjG2W*6iVGQ-%I3>?t5Ge^sRE8o`Aw&GXYoGf(&pG?7^W43A{Xg&f{yu*9^_=rP zYwfl6+UvX4+IJjuQq30Dwfn4CGW`0QPh7m}w8Q$I)clgF2W+T$?ue&$fAEwEeHOjf zXZF;QUk*N{?*$9bt8r?R+sB`L!H{1jJ~hgHt>GWzj;{C5riV{@sQ%g2KN@i5Q} zZu8#`eZTwkvAcB}IXI;FmW7E%Oub%PDq*jwQ zP5Sow`-Xh_)xme*=8->+nE6TexP5;=V9Rxbjyi8`<9>U6J>beF#%*JFdFSN9sCR1C zylYvXj=zjqI-}~Erw{s~M(&swFX_4V+}s)8G6w|FZme z*udh)Hg?@Qrr#6WUa*_Y>bm^#*;DSRf6QaaYx;IPW8$SPTO}?w-r6|khs7;!f916A z_Pz7j9xu=7RQHQMtIzzk#*9@b&-i5X@%QwZ-F9@_^vfsLxoG0JZ8dKB@UjlK*sB^g zA9{{EH2Ltx?Gyf2>$44y)tYif&1%=a{BNDJCjS0$mr>V^J#WnXsmp0sd#cV?AAEhl zrrc4v$)~Mra{P0fE_rL;W}81AI$_kC*H5~q?%p3gRQvjq*47`@cHhGudg{ZI_ifRz z>Az>>=fCq;tzp0YvuQ>5=c>#;bM5qnBc{Cf`ajoIyWqMDZytMRwb=_t^;q9^-ssCF zEqm;gNl#yYU(3}$jDM*4<;_=iK78XLYoD(*<=d}^*4gdKgSsDc=dP`Oski9-M zUfE=7|8}D~ZJXTqyzS$*Y_lfccEYbmpV@Z)h6e4D_cprX%(2hj`}zG}pXBayPKVzP z8T&--L+YN{Y)RkCntuM!pY;wt{Nk4`o<9G~4ZXXsJZ+ys-E|i?!lh_uo2PUskn#3v z^yFRcZO@E)^0)qGj|ESj@xZ!KH=cOctW#h3`-~@F-R-l*ubn!uTALecuh`gVj|U&> z{N@Ft#{E6=kW(jocnfLqf42XzT}B`ApS2hjZzxDk;PUxJv@~wgOe*AcP zw;S&5S#|xqn@2y|Vdlk;_xLxp&p> zwVprt-5Irab?aWxzkbb|mapnqeZb(o$=O}mYVl7sYA^iOJmJ?*RyDuBcHy%_Qa8O) z_p}2x+|lZ|He;tHo4(lgG+0%a&)PqJ(3GF+o%`sZAD@|We(&v5AL}vTdFQSD_ulyB zaaGUs-W-2w)1&_U^}c$)UNm|18-4FN{-IqP-5}iQmG3S3rA3v`j{bPpgFb(HS)(rN z4!Ggg+Bd)S==h2A>z=mH6}z3cyzY^mURt?e=I=A@+{d3@^VmU8{(Q>3&VLV>K5pg< zpAP$Z&h|Ae-(L4Xb*J~f+b90pYEp8})gSdb_lLjVz2M(}{{G~ZT`xWK!?)OEw~IN3 z|0#QGU|g1s9&CfgD3{H*bserg;lG}5D_)JHuXuH_(Y%(+&9`;`>-o0yY704D@oJ;2 zDubJE>;BjCZN;m>M$@aU@(LUb`HHZ&u4sf0=ac`BMFO=Pj7&oly<-%I1iBde#Ju=F zUSvp&(~GJ4C-7!&jWM%q{!gDwlheejF_AFv>HzCpUXt-&54SZjr;T1s1HCF<9UL4~ z@#_D$I02Iz$fjYiwpUgQ!i~0d8Jhk7ByM^!ESg?iHeS}{2HUy}*ZzNfG1)V{I8hwO z=yG#yU1s#a|M|tRX1v&Pi=%v9ZmO-z%pdqay_k$iIBs}}3dik_YY6ja?tebjmR?Pc zn_gY+xKWi7^(12A#((I-=&Oa}rdO9cZf>fr%g~1ZXMtPfxbf-=$1OO&B57G>&!`C@ zSrk8eXQ)>42lDtb|0BaxtKfx+E7dH!38D^Btx-o5q&?%xZUnTjmgPUIgleUai2q}r zYK4e{P>W?ZsDM)}ySeZE%NN%=@U|as`Z4k5Z*2w^-h68HKYK4d?)g0*dF-}Z+P(1R znb)uF-DN`e+dlq!@~&HdPQ7w|qf7hWu=eBHjfTBCwbSoi>s9%qeZ3dz{CUsH@8Y~`&(zUtI-*3{QG_wRP&n8QEZ z+^gGyy49W^vg)lr|M;%yS^s8FnY#Ov8%E!_py${p-g#l@kyCEGy2d}_wxmWK_}wvQ z>^<-KZ%-TBZ%e1O*S1f{dYZa?T{az z-S5BwtNT9p#q8zXmc97Z^Y3PUoBraRmpyRHl@qRc=duT8T>g89&maEf@9TEIcVS^O z8-xl=4{Pkdt+*&FqYGDD2%}J~xDau&D2FJklqefCwW>W8OoE5!B&7QY^+OM#TR` zF@|lb7GtdK-lFeO-a#)(O1_s`rxS2URL@xKjvb zGjC4gJ*IZ(*!rr`cR#b}?~n3#HT~_EqXs_m$2Heq*Y8(vNSEWbjs0%UC4U`o&arzp zdiR?*M*TbF!a-Ke!&_Fps_?J9EgQe@~nD_ZyuT^l3QPT-~7G?{n+!cemHz znSDRM_o^n1JE)xYJY z<`*^J#~Rn}+SYAyHEN%9VvCQeZhigmd*9vo^r0Po_YUjOpv7a49`wLDP3j%Hd`Z=3 z?D4hkd~)wSo-hZ0de+BnZ#w?|O!`E#W6R`>uFLMMbJoR8j+(q-PFt^vt6*-q^h8tzlI!T(Y`Z-=Qb&o~qt^)I;@mIXu7em%bCGeo?dA z2~Sl$Z{AHc7I!tqe0ssTU+weun7!U^w(8tg?=J4r_Ku6+ShlidYH-Uj;}e~iwlf!f zc}tBYhriMFkVo$w@kINN@9UI*oEd)4#pn>TrBV9uI+?czIXPgrnE z?SI~^zF*&7E$mtUyY#{t!w>oU!EgTk?7Ewdow)YED)W!q|LbdByLa09yUzLNlYcsN zeEpe4gN`}zfs?wuw*Bty%a=XWrSYr@7fl|LdamcdA%9ML_>shfbuTu1@QP`B+`syv z&*#j)aF3qbu5G`dxywu2ZJ>iSJ!@>b|S0AKrCH`y<}$apbwb z|GMPx_viMkKC9ZovpXF(=&z$U9eP58<>QCH|G=D1AKf+nMSJF9tqz!)nswsrC8yqE zTx5Oz?3;%=E6=X}?ATS4FMguwj2REtJkq%4ygRnc`E6vX+3b&Ax~65Li;TxkY(KK| zmm7areol|CMqRPwg0bIy+USN=cP*cKO~cDRE8O#Wy7!Q?7Egb*`lapfU;otGiHpn$ zd-oZ2+zZEVKRokEzwdAUyY{S4_dK-DZ(SRFyZ+!a<~(!xk0TeoJ8bWVo;`7}O zoPO%k`>$AXZ1>H>*=@)EvQL}a4_te3)4NZ5{g*_o_giajL*Hy2(_+kx?LMxx z&x%`HUo*JlwAp9%`S;4<_pfNw_1fCWar^x5qb}~Ao#y^@-EYtCQ}w?02fx^mb zy*TN$ZCAh3<%x^t+}rD(i%&b}hc2_vpKUa+e!|vMPaQbzg`)@U)8*`Ao_V%w_d~Bc zzU6z}_Idf}cRnybS&@10sdw*Rv|!fl_oog!wX2sue!u%(db3mK-j`qYbo*(^alH%KMX+?SZp>${Gx ztz7rdS9LXRH~$S;0r*1mO~d;IBq;;`!0AJ?uoo7Sr_qVtU} z9(_%l2UhQ~>yp|(pR}?5^rt7k`c3_M6OD0Kwyb*O%7@xMcGn$so~YliR*xn}&2buz zUpl$NTmAmG?YFNsZt6U7`>lO5oj-i(o&7Jr_wq+-RlVVX`898S^`FH%_Wk?BCz=ks z`N1ClWIFG4bBUE}*7Gp|nH zd(i2hR@s`r_OGMfc;t+ihwc4AtECSg`g4yFKfiJMg55JCs@;A2uDd^1W$`YDe0Ti8 zoA)`t<=njozI8{k+LkBI{$p_WV^$pBYj*yJ1Gl_>$LrlkJaS<5XZroQ$_KII7%{XxN(r!o2 z+Ogj`FOBP5Yt=hP99v`a{~kBiH97Z=*Efzka$e^TULW~N*MWupty;fw|Dl8CuKelm zZUb+5W%FA1>5nGgzx<-E6L-I7uWc>H{W$HueY&krRC)ET!>-u<_9X{**t6lkjgQ{t zvxPHmP2JFQ{mfLA-@eRE>b<4f5l_GU?=dSz4PW_F_mkfkx%{I&J6FH(w#*d^&VAy^ z*^TGCHR#b<_NY$7-*52Rp4Tk6`i^QXPk4Mx*9RND-)DGX$!^~L$C-EM_HTM~>(&D| z9Qa$UM^;bjvbpt!YsXBkzu&z5_i3Fw`sNcJ&Ubw0j5GTu@BA}0_~|dJy*upe>)Q;u z?elG0z8+uiir&XvV=R1lm%qMUw(D{GwB6^b_Vqrk`O~tO8_t=q?EK3QUA4ORx$n1q zdCv{&wtZAMqJ8ao3w9ecdsYXhede~)hvkm`X29;_@ykPq9 zc_)s})$DGy?Q}u!4IjV1|B8jX{8InM9!=_>Q|+6d>fCVS+E2EQ`{cG|Ypx&Kz5eIB z58dzFy}v(sm*>LodA>TEfaOd7b&Z^&X{j(2jIr_L`4*8}@V%YG( zhn?T)mAU`4JE-9it6DZ5?Tq^4$2&ehzvmx~<~4fd)3I01oB!~*5k0ov^GCV(He7RbJ`Qw)RKQZ@%`!mA~E8bl9f8H+=ECHRy^7k5*~kYu==9*F3WB^M!N% zojJZ+{Rsnlz4yqtvCX&knfme*Q=U!SaN9fgHSq3z=hucuce);9+=kab@}O2nUG>c! zCwmVc-J#B`=QGnq(pE~UBy;ogvz>t|&9yh)Fw2hZ9J^$t#j$V4oVSVOaeAvUiE_`)RvyZ>{ zrhD!Eh90}<<_3qKwxauMFZ4O_^%c9XThr*zZ9T91tJVK%PFv7*?%Zomd*X{z``Hi8 zZTt3lH4i*$Y5$h5EEsA$(ssuS4ZBud-(-BPxhf^=^JX>Nym9-C)pLFy`tzu& zhkW|WTlf6j31G}x?y63d9qpt6rKCjP)n?!?cP%&H?#|>*$Gu$j^0xP$Ja?Dphfip?^@o0Uzi{}DHi=K2E!)1nq}GiS zce(V^>z_aUw}Zah@ax^<2efOs|LJ#r-go=bKcC;wZ^foHqYnD|<*^q%TK)H?cR&6~ zy55bOE@*Xr$J=XUJItHgchIf_-&pzjzC&)9Hge3Lhq@;|_S*}iUK)DrdnZ@hwL#-a z{Wo2)Y}=}h)dv53bN5$|8PMj-`RPCRer}=H=#AMUrjKd!@sjkJXZHX7q(72JtzI(V znVE~5Ek3``%l)SOc){&M$E^CTNt4+(9emQ7_s;+7n1k+JQM1W_8EqCkb=yyejW#BF zo1MLv9P_{nEzbJvvKdQ18aU&GWecW`{_Ehz+n#=K(af3oTBsr7Qh2PJ23Z$9(u z4aRA&ZK(Zm@Be-M=HmnQesaGP41e1^OZwexi6hn z^~ZY-?0L$uSJ*%P_P?6T_t?7d!kYJtT=Me8qmTWm%i6oEU)bh{ zXEwg&>oZ!-tozUYA2pxdZ?8|zTlw>;7cEGgGHzglukK7wykY#D{HoX5_CC*ia^sg@ zR2|TF$}8_x`+Q=}MXy(RY-z7&x@;W3i!*%i(8m|<_3NdretF{MO;3$j{prgOJiq^W zqgpH;^V8-let3EQ_Wf6WvV7{>3xB*lyKK;oTORu7;qK#)n|#zqFV1cF{fbx3wzJO} zHn;bhDZ^`?-TJiee@#`bwZ6&;1E2rZZTI;2^S>Rl+ez!T?e^1#sVlckZSi`OF;h?e z-ukTjm>MH@Y0&YomhBGzaND>IugrL3zcmB5C7x^X@A=DLylvp}Mzarl_l!fHx%R;> zxvtiTkFV%+>%hfh9=q+w{eK&9!!8rruKW3qJx?7rqtN7)5gX5(bj_;%_x?5Fu12Re zs=uT4z@AILIrp!l*0!oOXGWjjHr8miW#*B~YIR>U#c6!Wgw21>`o8nXyLKJ%!mMen zr@i>mw4N_^fBxb*b)Q*u%>gSXIKSjNZf>&R)dTm{yl8e?F)Z;_n7w{S$Enc z#~(lQ+mnC1enRsB>mKwTu=*@I=DOoYcUbyWlZ*S!8*@?jv%dMR_241j?b3AH{hoEx zw;SKTY}@ZmuYY#fTW5VReD{$P|Ni>3FTP)TNT<7h?>O_*Yp*(OQh#&DiRUh?JN=SG ztC4Q&YE$m}>7~YNzh1DHx9Ws}KeW1akHen(ciV^_e|$0J*q>f%@>{p23qINH?@g~R zO}AJ&xXP)AH~e;Zhtp2zw)o`xKU;JDdB+^PjeY_i+=R=Y;keikB*p!JY&*9K8 z@+P@vXZN|N*V*Uyy`q*sPP^=g7NHDlSVc zULVc~$$wT!4l0;#_Pfl2f6V8gAV4BH%idX*rB>|EskiZ~Q#vgFy`RZP5lZv1Mu z<99jvuif8aRVA*OD|^rXk0!XXuCj1SR#3Q=rDQ{DBi6MHBSzIXKYzga?JvCcva9>y zpWD|usr{vW24?&8zPin|eXj0%UaF9GoxGVd^SEL;morm&(=&6n;U+Q}+wkmjPdfMH z(@zdFU52;@E%1saeLD2Pg#23F{8`BXIdB`uQA`A{a@c81-OXf zr&u&J1m2Gx68#o&MHTmqh-Pg}gdoit;`Zp!*NFR#xQB$l1~=N#*H8ft_cd`+4Lt-@ zj9BQPp}A+oi8b^!zO9HQmZB>uLf<0B*KpqwdIZ&BkakHRQSDj*#+L5U;LQbNCNR2% zU_|UqEF=y@NER0Eb*N-{h`BI%r#3z|8Bup$N$I2Pmd?L@jA~XP_ zf$>YOoyyy3)5#|Cc0QB#GF~pnt2nI{M!_xE*;FnKbFkn`6X|Ti zNEUcOiTNq&E6Pe_Q%NtK$hcYC%%{_7&$AOL(=e?>5*En|N=w}Z!^;|JOwG!ru$XLE zoP(&vgVEu?<62Js6m(VL6$Em56zT14`r+9m=z9!?7}M zp-{+WGKpjg@-ujKhwcq5`HD2u>ZX%9*E17#GUcTTDKA^F%}l{CGjQtcSBQc|WkIE~ zR??;wWF}K~B2h?t2L35yW%7B;EttFm5v7UBukegq%BKIxd#(+|r{O)Ew1-VO<2X** z;J-r4nZr3@Z6>n$f`uV!iDagbc9M23nepso+DgO-O3S*_UOHvExdf~%VP`Y>WWIpS zBkyIcv}@&3yc-N%M^sYX**w-$&dU`HC^?DSRr4_Q6b>gT1GhIDX;x67JC^W@@;C@X z=H~k5|fuBj-_SsS}}5 zreG!Wu&ESECEb*hD7&dJ#6f|bFZkjkVCiue+FC&#}+%X%^CFAd0fY@8-4U=egXSRmpSgf{qB zXzk58Gn;S=xkQ#U3}>B$?(LL^Am2iSmS^_^gjM0hL**N%lPM&PWFDg_U0mnq7@4%4 zPPv(cZMpfpn_wLTO?3?AHY$HbA(26pWMvX?X?7u(Po`1`$_fP=KF&4rth2`Er1OfL zfuo6;#ZCruNaymI9KvwJLD=eK?R+xkE40)NuUyD`4#F2$ch*g(v+07DNgBCAHtXb* z4ySG^|JM*y9?C{^Q9vM*Kp=^I42L^dUasI|v4ye$r75RpD%S-TDZ*|io6KQX!##(H zEweTq9_aj#O|n4@0$QD~FtV0i@KQLDB2I#eQ#lwO4vd*XA#LPx1y)evdUa(_n?|&n z!iHHeDG!JUxscCfvsNNw;4G0$yZkG()QzKJ*7FKECz;P7&M;HyTm~5sH<`}BM4T91 zXsH_qEZfQ^?3|m(A$oCeXwOk%6GtN$vBR$@IeApMx-k^fMr4=9RzyLJka5C9$Lo7iP(-TO`wzS5(T$!+S&93B-4{SAZ|iq%(OOObb@ti!nW9<}|Du zb_WG#ux1k(JCjQ%@l6F6Pe+mJ|R3N7nSBL2-Gmjch1w~^X_EAbMb8H^mZRx81qo?g}rqCrL@pT#t3 zI-4ZmadS{~K4YY_lpJubX~)W@2Tmtt!5|9Q#Bp@D(gxXJCWDRKM6}N9 zLQFMQenkeS2;36vni;Qv05g}yNyD_V*#B%7v=SQ)n!%fNOXr*LWD{W&tPep>3gJ|? zfLI~rI)wrb0sQ&G)XSPV4u!~x1aeLW;(ap>1Hs`MK_{*rF7QcGnvAUBqY6m`;7BxJ zGsR)r$)ym0B7Np%9S<8-ieI#PSu^CXQdu{H{7lY5X*_bh1q`{wip5@=wk%%VM6jvT zhDIW5BHd@$S&)UuW@b{RlgpcS7UqIv7q4!SqE}u~$wb1$DJg;EjBVS=9Kt~yEpX1r zxJh^`J~G$Kn&A}*Y!#SQk3+GYN@vp8jXZdCgr8YNo~(6?9%ALM$fYQ;Xd%-C>EzM* zlEa}4TNJ!IzQ$n_bEaO_4Dkmx3TOjYaod@EB88S2FP*be@LYww!}}<`teJ_FRx+8) zIY=kld8DzR4jf;xa~Kvntl4w7DQC^fa~$H`Wh5MO95g&1%_oLUO|(JE$&|BV<&Til zBw*ldbOS{hII!8_bshHzBxs9|kclrOZDSrGeTbmna7m0I29<|DM6#}O6w)I=!WU&h zW7flukV2u@N5GPOams8rQuDeW5`9FRIOPukw<=ILkq-&AF_z3^+KEBq=DtQTP@5zEk+XxPYg-jNliQ~cfW7We!vu;2yt7IZmo=>`Y z(=B9>OhN_=91Li_Y%b*>3a_HsYoy&!R*o7;0I$ zLMF#%wsGr5cwJW>Un9FiUfIa!9C)*o0|FH1W*0Fw&g{vUO&<4(Xl#$5uQLTFpG_Ot zBoc0jQ`4!ufxWBXK{Y9MV?icd=M@MQ5n!8;OA6brjb6ws&V-hi!2dcqK6!5GWt9+9 z&l{SvGwo^db3d{hv zXpuG5c?I&6nY5YAB|u;y7DWUE?Hh)ZF4!65z}fg3?4r&qpye#gqF^{!QJ961G;tDv zO@OvU!pUPD2;E+yv7!p7mPv}#9_hmXBaMtDGBOlqWBLSZdf1wEUO_5NWZ?z#ATnU; z$WeMo6(b&|v!c!GLNBY7%cKyOBvM8u3x+a}#8n}S{2>@DH-ls%8%J1%mbyXd<=kv0 zn+I|_+9vZZ>>`bfL&AfLVo%R>bC8BEylfiYFM~8p5`k%&*ta}xh5^y)Br;Bh<*ocq zU*)h7dA6*b%zB8m3h+^I97s*)%`{vgmM-sUk<-%o3Z$s=plOh;gm4fxX0XEGIPwVW za|s+6S<}$!8p!4vOq40p}u}6_r5E zjOQe==^>$uiVEaP5a}ds@N=kqaClFvSBsU+;oO|UK4+#J7`IF5bvmTyEd(W41ni>K z%PQfN=777;p^s-7Q!9os$cnCNUBk88{xvYm1H8J57 zkg#8&SBr&>&d8#Kg}kes&ZLl0$HtvR-6R+x1P%NvG}UdH*oRE07nGSu+ap#lAajqH z9oZ}X6I+Pf{(MHR>t3P^lgaI^WGi`4}p4ODZUFZV(cO3w}2 zC>wB`d(7mI$=S-aIbl6;wT6LONST%bsb|tg&LV zPVys=xN<0E6e%4?b~yZ@arqul1OkOgbJ4;eB;$*H0ze_gj1*g2av@at1ElmDeXc{b z>vHN0B_}Z-5qSdY`SFD4N62MPu^8|wdV&=ZgPd<$$k184B|iax$p=3DQ?k9@Ae@5oO59p7KY?AZSGaL{u4aE+_U0kU1&C zmh*>}oT{np350=pg5&_opAZrR?zY7lww6QP5RNPfaJS)QS$V?9OW|}6h5%=699$6m zC)19FBYzU%h==11=WTHo(RoDyeW$ux2!sBY(9 zA?dU7S5PFLqN*TNJc0YorScwlhzyS6ChGHS{wwrSEqQDfs0_;De2)YI61F%WWsy5F zP?VbYQfw?`>!n&yaEVrl6e4sKre;w^k94+$x)%pC(!de1+Y*Y?bygHD9M8>VkQPMH ziK;WtOCkzEdIaamWR{IW(DtYE3e*aqY8Q1h;Hzz{U~s+(CySARl!>(Y9s{~1StHeg zBQ^*{EL#_WKh9<80xH2#eugv)m_~zNQF;YDE&^K=?;tgZGZKPml&yjZ^w2+nv9buD z*+r|DYQc&{<^aquDogDY>TKK`jx=@>zanp*XOnlfUaG}Kg*Jk5I$$7Ii>l{zE{Q5% zq(jKO`GgUIQypEH7VcF{qkJB@as-=6FsCp$6IE3v&iz?7=(P1xEfmh?kdFXYokEHT zWJVhKiX@7Ok*fmt!0JLT)q>-7E(`NU#}L|ki0r}o&7hqNc^70LdAra{wK%9gL8UVM z2Mh!eMH-o?tdm3X6R|CN1Ng7dvTj;M2^4r1P~d<%Qhbn$y1bNyYD^S*^KHBM_jJ^a zNI9FrMwg(yD4ENpDXoga6f1%Lw`7_xaK+zx7yuf9kl>v1% zD3!$7kE%QpFn>1_K052COdlWb995Ve; zY%0aCXuY}^^gF^Tku)^ZsONFeIhsR79hy;XGY963_w?crp`5|6G6pg(*^Es+OBPxy z6Xe5CLWUGIWxiRv)XQKbQ0!o3vWTdiybb0F6^PIYY~MqPR}9$%t}DD`E03vBi%@Wh z_d`Q1QdcM(pfe6ii}4>W3Ln|8kTV=5UV$212brl9P6asM!0V%M86GT&+6y=y-bczA zkjh_yZ4Z$l{sTS*=Nk{T_V^Tp(`Z^l^vjl{Awt#?6x52ou`HF-A%~btVzx0dtpzl3 zq>(n}9hjU%sr)O@J%yx661BMqc(GK8%kq-QxnV6Me&lr_XIUzL1^7c07!wg7Dp9hT z6bL8uAty5EL_+43Rkxgmsr(g|Q9v=8n?&jjc{Y?h80Zv2NeXsK5EDtZoE%@Pr3-3# zHDJbQ{6el95xWC^E9-!Pf}mVp-Et znV!mDfudzpq9oz-kYPw>aSTr&OPoi;3q}GY)BIP+si4YVfmk$$b}Tm!vJ!^_INFS5 zP;)BUFTky^ggC-W9bMp9hHN@ABsfDMdPmDKRxE1D!3lvWP4Fv9&Lvg;6{urEn24%I zWEfEE2w72mJX(2SD=2g2Lq$2+RQW5ARzN*58nQjq7w5p5ff-N2zCl4i>oF@zuP%s6 zXADa)>~Q92)54YYX;D;(5)KSZ;7kuIO-C2-M97EWD1##bbW*^E;o=m9T_o??Xp7)q zp`~tApd>tSyU6QMh6Hawr9TMR4ALZsbL3i(mdYFG{z9WaID&+M(+#Fepk{%J%=uSnoo}$3a+Dpxm<^ovQ%1@{ zg%hbCwLC7m2-sKXC99I?xMxZytNY>jzDjVOI ziPAw0E=cVWG@gha5gAW}>b;mJL_|rFU>1a&Vkx~KWU2^l+A$a;L0GtXhrYs%tV%gU zQVv3T0_Q2pr2D1RDqUSv}57)0vF7`Ld&|T zt;;G9)rR;B4d*zOq5#T6IVTErP_E6tLQCDqI3!VZm&fr3JTbf>hCARz29p0y)=99* zKSwWBgNad?;^yFZg|;CBnn&RfCU2xraEFF9zKI%Set1QdZ=517DC<}QQZZc6!zE4N z#N;SMPR5i!#J{1jIC~O>RAM@+7+@Khj*1ZiN=R^PDsAH8A(4TNIK;#}MC|TmpJQ26 zr;+j>5-CGM*Hz3z#I{uS5V~fSkYK;BWtx%TPELxH%OosV0H$HD^f{5BMoxN^KO*!o zpepc)ByeD)vaA_7sZst2;nmAN1+gI-5=E36X-IVBqy_&79Q4FPToA0$u0}Lg#E3NX zDgG=@5-KB5Iqgu6BlHMTEd{$I&`7Y0yo?GAlBkVBH|zc4HmFQJ1Zn{?O>+m(;wmA?XhDvg#lOq@YoE^@DFlrkX1Ou3-c5%#l@ z4Z6|8(yaUyIYh+h5<(X~a+jzh0tJOZBw6&zyEwe^1BYDsL|^$UkQ6|Y8qIoeQ$$7~ zLZ;Rvv|54rO7Qb(UHQac`76)~CZ^G%BZ8)UI!Vlu#XMq+jZI}y6~YFx$o%RE3JFA% zt>f63K^h#xTR@+J)B)^oC(TF_NVy0!gta zVdS%QUXcf{h^#RhiBbEF$;W6a&VnyM1qJF?kdNg8S#*`_yaMOb9Qwe(4bfmdw4$Lh z19M9;sU(Go@o}$^oMPqbMzIxcy2d#e6fh+|FgO-O4wa8#ydTEtuwS8$F1uPxZ4u-&CJ`h^_ zK@nT%D>89>LTMxl^Z2jOvQJC~!qx!_4;N2>h(}6~=4|80iQEW{0p?$!WuNG&2W5+? zyJ*A5+!AUY1JeLep&$Sw=2)gm1TyNAa4=R3(;bu8QV_-(m>PqP7k#y~1`wn1!`WQD zLqEFuQSnUEw7`yF*bBzWVMHRvoPn%DhZ?^%>eXj}_=nHOcqo*w*;HSR;Y~DxOw>ol z2}-X%gQ~nR<!r`0@XVtBD=+9&55juO)H0T%15tW`h_=tpu z@&i<0viUZwI~}{gG&Z!<8F}a%RVG-a7*B+a5*HAo>xi+ct(`vJ5U}3F&+Va>J;d$NXh@K^o+Nx^2Tk9ycilJMjHuC2ovboHT_ z0%=e---g*y;jbv)b4Y+?hZ;Y0=JF6Ou|7fXQ2zI!=(~~!#3nKn0f;-F0u^dJ0Fht9 zjp-Ojjk6scG8?w)HK<=LlvdMshC9Xl9|O;n<$hk_va2rY+~?Yh&OOPgU+eg6YfJ)7 z93QsY9(m_053$1@XIEN-%mw}@t76Dgc-AUluG$U{cgM}BkXe<8u5djYnh3s5n zjmzNC<`aQeVn^cPnJhe9FjRVNfm85Lc1W9G4q*TuE$s+AX}zncmY1EO^l zQg%5u%+!lw^M6!dc0#i#M`lEa(g79G1<+wCc>o<_6qtPyeq;&B7142uMX(lr6mPS> z)4)6^WOOQw0;7Z8Tm4Sl_ zDikY{kg{Us%w!30!XQw&%ED7Jh!8xZ!qefDz{yU}B#Q7@)e?K4=7_=QqXGkFVFb)V zWKJW})7vM6YHe0_N=5-!hk{Js5hti8x@fX~B};h!N0mePdsQ@x4h|Nbl2s5o1KSwo z1uRNV7SV}huPiw^1FVLQRV-TV6%>oqD~S${IOvB(4mpBNZ<7#ObvU_!5Kuyf={LBE z(8m300jY83AZM#pq=x=C7Mqee6s1PS5L|E~;~f#3o-qhpb$B<709Qe9?I^8bn3ElQ?do}a;R*Pp@|H=EVoi=doI{S$pZ%t+@O-&04^#wk=u`e z)3XJkR1k)tMNRP7c$F#|Lm6dV?1L*Bsvt1r1y_pIkkAR8CONs; z>b8JODd}8j1)>rES4L$60&DgmucGlIseG>si6-r0AhVw_ z41wbkOQo1N37k-DOxgFp6>-LV#nk}P#-m|B1avkHq9^r8%{ z6z9mvEha7JDH%oC9szK{oS`5lBj9w84p?(BX*rmog5-odfl4Ws3#EenQax$Sm0~(2 z*$K6>D-IQ1D&XKwD$;TRUnl6$i0pP^pNR2xn3$#u&kVQ|L8V6E>6wJ^c2F)Co6v>u zrPFdD)f!ZvT|7ew8dio^ikp>GTZG3d7j&BMY&dh3+7sptYfh4M937A&P0X0;(8;ck}-&$jiQSatpyRK>TM80 zscb$ICj%91Wx&;6%!J0Boz)gN$Cl>>cIa|U2tN_ zaoN@+I|GU`!_QOkB7CwnnNYF@K%@MU1dUd`WD1!2vop}RO$$Y! z;YU?gZ-6=2=rF2a2$LK!?fke|W5x8)HW3icVfi67OWiH(8Jw4zN{ z6I>uqf(fx4xSiM;_(Us8N>#vtA)=!LhNj}!Rl%%c8WV?3&l<9=+w!S73@}o`R)mHW zBd&o7XqB@DZAzM;u{j)cbgO{FCkVkIvlS8?M@i%6?eHT@x)FhnwN*7rI)E;CT*cI5 zMy+zUlNvg9V^E`|vFNA}LM*yOMIs_8IB40=X%f?7(Q&ysT%Q?MLY$Wq(Jw7Jqv9xy zn@~8x>Dfb@W>71inG>g!(kCvc)k0K7a|MzWCsk2+8rdH-Y3U+3e$A;`gecM=iwI<^ zG24p~oi;NqM8~cD02sGB zXp+)GrF>2<2`WYb8m%{K)u!})02^lu+O#wj%BSQowM7*h?OYNzZgbG4r3E&Au_>8C zapyu9?sxnbLrjj9#z5on*Opvu?P4}nHCEb(hOv7Hl>)ApBc+qtq%=X}H&t8F6v`>3 z5ID{lN_F=|k~RVyYo}=7S1<;;XStjf3Gfa{j+gd-R1Y(=tpShSw&?g&+KHgK2d4(F zmWN}l;RW^vj+@3f)$lV*thqML2wV(2C4&&~a5xk2g0kcIX$&si8F;K})%a;RY7xvK zVQvNrM=@puK;(c#o1lj9cx*~G5e(%5*>l_r&tcO&KM*!vv1$x;5M4-e+#XS?`I0l) zm_5Q#vkEBGA65c3PTcEK^u)AjL=~%N6~biMO+t;L#yw>$kBR%H7;u^twJ_NjkD^9W zn=~+vp;nW}!w>mu;~aAd{#lDZ%s}4xFA<2*q-|2xisd=>ex6cZ3kT z7$}-xwF^dga?o^-5HMJt7r~9Dn&3im;|!wIJS)y#F`$Xd7-|vJ$hu)BSw+Cf5!4t3 z9{$`Cdzerv$35V7#qfhFRVtFLp=1O{Ph;|6_=zQ;31i^72Mi$#KdLftH2OCTjxz>L za=HkPRjL|2U9?mLCqZH#xA)=8;r2| zsR!H{s-aW@7pT_2C_P4Q+O#y>NDT9!MoWue7@;X8F3bRHj?2tBczOmQ)XL`f)re^k zZ2}xco4Dtz!jT9TT3&P7)HGxlljB#TrbF;Tvg1rbo17N#SbJ3?rztL>4NRyuz_TVf zdfNX{(_-07zZy9m0vD7W$4@6U`Dx+nVsiaz3^fH!<+2OG65KwDHqAtae)c%ip25WHj)evV` z2fSJV4J*#Zwt%_z4;MG zPSrxGe6A^_%2Nvlhl1e%gM-=1%!<=&pe}-Am8!~A3vdXA0|XAnN^#iqgC>JVrf3VI zehJ}bpneJW9CFBZ!W8%{6Xv(6uQ$4YAz1i@dqg>K`dfp+Cz(=k`U=N=3WYJETf|EN z=L@h=4<~-qv{Np{gv&wHprJkp&?pcNK;t-S%$^EAQN_RU2`0=r3O|Z>A`}K&;00uG zoGoZm)bMY7))g}kmB1lHvOqBiVzy%2;uz|bHbV_?TzV;`#!yoT94#JAaSXNpqxyOi z{*6yBrKpa!bnc4+U=AE-4B8Ae$&KGpv5PB!L-=bEPXYl3tbyaFaW!iAfhDX#Y^Ypz z3FGy`58`bBo<<-Gk(s#27=J78r<&}vP${2W!VL+k*x*CLL~Dxcn)ZLxTyA_}Yf6ov z26v8ZHU+*xg>u`1CO<6{%4L>tr=N<@d|(t=27z%_piNH03i!MdrqwG#1HmRiqEu&aw(;htz-{kKgCgoumy^!Qyewz|ETHV_>2-JS%yE8-x9=%}a)SrIyuRs=~LGo2DYstZn7E0;}5sqxZ&Q5Bs~gTT0rVJApw+`YtFsv0Tn zLlgHj24u!<4T7asmzhu}->08aqoj+_Xp5_W#?jF!O*)#?X|h=)H8L8&s4Sk2PXS<@ z321jq6EI$#szkIHRUtv+_5{IA>!}maSg#vPL<^A=FOBipEp9AnO74n|@jX}wl+DJp zdyqU8IoXt;n&mA|=ATQr?KZ8`xn`|cJCb?lcyv_ULFkM?R}(IwW;SG*fL21s%3da( zo(4>7p1sZ#}m3e0# z8hIch0z!r^c;*N&DRWd3Skq2%>y6HXrH{6VnUT09XogWyIf?5PnAIlJ&uZZK6griQzTIdD=~qy~;%Z!+&(88`yy-%V6B1;I4SYL)3{MR18W9b2~KqG!QJ z2b?aCkgh(e;OzpQP7ux!jJh__tW_e`*~#>CJiziwwxF{Ulyd~2u5ADq^nj`eyJfR=VG zbo);59svWs_pG6l^mm+*ye^(sS1mJmU>6Xqchvdd4Vq2m#f0n@!> zX7F?_$ma;cov;sqkadDG8C{N0F&K+wpnb}!bTlXP&E)`m4npAp(uyyCfZ7*;=ZstC z5(2@YBT&Sx9p!O6QMl1nRQxsIxhq-drkFC{oC_w2gN-pk85=@G>Wc{S)c9*=g%^KS z$JVAe39b==f_|$kOUA{;G+Nvpqa1E;3@!iSolRIwmy2Gu53x z=~c*%vpYN~E;M4%QC&&J#)Q9SVx!`(nzG|OKlH6?6Igc++*TN!ppzq-l}Y9jHX&rk z(GozXVHI@iyo8Rm3b`Lz1s#`T2RaR_p!*pmbgWg#RI>^?E?*3EI#vPVK|;q`h0Hap zpyQIBK&NUIWhESeN$&V-)+%JWSp^-Jy~E%)RjUx_uv3K431&HB6*Awff{v|e0z6f- z06dxkD#2sTLS~$m;PHEzOf|>u76cE@LW0NIh0HoD!Q;1!*y}1~hb-VxHAm1P3x+wW z+@07i41T|m3Fk_shi{SKv6dk-&q~tcHw>9`t`r`)+b}#qG)JT-)6Yuq_#Hzgo-2bV z;;4{v1=k#fr+!o@j|B{V%aEI+D}_gaR2Uv>8Zrs3Bt3r5koo7z;E6~o43FJ2WGY$- z9=~bGEOh1YWXl!Ab5yy~sX_@JziZ(9$seOx$AVZYBt1bsN8rhnvRpS&D%LU<4(iTxJ{s=m>tpHtMHWmJu;o*wE>Z0S}fa9Z`SVmfw zoJh;0YE`sx8E9SOuxW78KDI);d|TdQLKtdl1(AwNyaOy6z_Avg!An#8z$~4&OS#L|u^DANOT#1xUdSZ4CTToUI&iDOi;BOxDkKb)%o;uEE>7HyG=cPs>|P-=)p77}OLoLCxL#?1 zlTO@9;9BQ+yNzv^mQ2Gz#3s(i;)Zaa%m5U2w~&czMS%S3i*1>f09x2sWXB?C>WD{{ z$!kS;{OXf=>p1li)gt7V4Ce7gi*yvAu65ArYn|hFjo8-di2SgfNsgK8oJK#%&KO7Q z9DhcZ+3V8s3vO#%?=%4JoPqGCTTYLy{w zU2trxbQmC_NkJ$huM`~c352W>$TW2cgt&(a$5>XwvF*}fd{hrfUKlTyRLq_#WRkiB zKB3}Rgi0o21QCXH%-V>|VyhvH(Xo=1Ac8{P%fb2AumWxlYnwS<_`j;1%gSw--DM%x znA1f_!$3z(-NbI$ffF}43KpIEELv_|87OLCHh}K!k)I00!<}z@M`g zBJ00;#O`Bhx5fj?(A5t;o~1IQ~_X1!w+j2vj01VakuI;{bl z3`bMJEFVX~N$5#&@Q|Nk70eol*uT}`2^Y1 zhXDFnD~D?AgLz4VWy1Ym)fb@9FK-;NL^y|zavFgveQ;V~_Y95J>~iVFpF5;U|xXd!*m=hBIXchD27hfzP z9#XGSJn%Zm<%JHsEGKSgaN_0q<>!duz^W8c34r7S0WfsnWodDA6>G^)D43rm27rn& z72P&ygHrK=+XqXF(_+xsK7{r18A-W!JI*=;IBai$9#ec7yLD)g;^oSXOGw6&;-S@t zcoc~$8!ryrFkxVD38WN>sWdq806g9}VoC83JPZHL!n&igc%Xw50NJfWgBK@&+17=Q zaeHqpE6(8aa|MALxPgz2tTbqGUw{H%%;p+^e;P&hY_;dhXykqL>QxErTqiV z*U0H6YPm2dFPM;Isc}R6s=k3_Tepnc-eP(2kN^#k-D3Mdsxc%$mKvvjTgSh&X-4p3 zG#tx}hXCR(2Ol6#pkaWljc8EgzJ>)rUcuNELLVFF0!e;EdW4RZEHQ3q5aYfHw~Mo2 zRbHG@L3H#8?QMb*k2pY?C@&EkV7N?t>>N8-JfvKI?Ge0CY``++h9+ZPgcqY)rP#_u zN?I5P2-uKv1v?&*pZHbBb|F7LbXH@`O{Ba@y)IQ&1yi{!V{T|O<^(Seppt0_fE1eo zU`W9%VU9Zu!uF=J{^EjXHOgEl7>?4#zitE%m)wYPFaWyI6(q>7zt{%vka-CCiQomx zHE{G5ym)l^QP8En{DghP#m=!q#-sR1e3Wtylzm}F5unHb{a>{KvH}F|@W;maX1agU zM&N|pbcFyBkhA#cS9N@tUBOKq6SbUAok8|Uy4*=w?Nx&Ny=I-%Z}=ouHlhQBmc zdf3$jzQ8PWFj-t|<^QUUFGjmcp%o%~IvGK`LFa;553F+atV1Z5-H~5*Y7{xu&r^{Guo;1?WWc|&cA-s?cZ_k$v{@-?mP(l5Rl?|4 zvk6txXxf3^B9m?M_J37J2MQDD_;aQy z^V}uzg`~%Jd7CodUC9a~=@s$uo1V;c^Y|#NBK^8ZVPK~V97F`a9nr7MdY3>5{yc6| zmdWlod_KwQg4~7C1o6@OkK&W8q69u6!MLDCCcG682JSpY`4?*;;#bWz7%M~R`g86fvOng_0PE-VzqWzxG6fS?Oey&s|rO~G?V%vb!XEquA(K;4Z%qBT zDHGq7;6q9#B0bhRXb?h>B~Zi1o3G4!^Z3Z$M(+UxFGq$D(XPyZm%vBb5W%Ju zXnQjS(H+&Uo^_y1F)o26sPKT7;-yy%43NhUC$I#~9Yv>W8-$M6t`$25J(T|o;0q?O zaGqvXpZ{y%n@eg};A6Fm8&Si4fR`U}9U?eIc#o8RMS{p;hoSqwsv{KoWv#;!TzKF~ zarpcMstdOv;p6PXlKF2H3C5JdSg`}qLmDP7fFvjo2Cj=ogB!Ph_`e3eTMc21j@81e zqK;iecA+G+#g0Yyf7M3Frm%2lyUHpovl1jYf{hHgx16&QnFlW+L9qsT`N{M*zXprw zuwX-t4Aie2-9h|cHKiAmLd6xh>e8duK?xsgAu<(SLVBokNBWd!hH*Km3O*!F1+VUc zlSl=Mi7Y{T%TC(=Rb7BWy)lbV$vmiCglK;kz9UqwV~NJ_D88N8L}K!%xQP+^~)fcSxq|(CDQ3)!O%lX0L->-8*>h^ z)QEIst{g;D4wAQq8>r!-lP6;za^rXjcx{>yyqJTC8k0^75Q!hsZ&-dTm2PQM=>j0{ zVbrK}0Cb==0niH`L|7)B@-;dNE=rJ(6V$kLtU%gC#0m^9K+!55u@Jd|yo83u0^|=$ zYCJlGOamln0R{`AEo^Cc-30^2dU@3G>L5Bc!jc=vOP~{F$1(VnHV*@Nbb${C76~6~9@-RoyO?$mON~GeYS##kDW;J> zjQNUR)$Ic%@4RiO(dR+!h6-RTmO%G^RYwQy#+`rF2=t(KC4B7hLv9~0z53$LziI@! zf#vS6J}R>d6k%B`h3@~VEmO--crXR5cPkgfLjDNMQ{pLER$=d6_sJEA1jQL6KzWVapB;)UgC*9e#43#Q z)oN5aQk4YIpR^pPX{T;nW)W&q=|aVP7^p_1!)*w7bc#!b;ITF$x00*ubaAOlH7-2> z5KAQx2nyOfVj|jPI+kEus#1+ir@4b9LKmlXNrdcHqDiL<4fDB5H8P#%#ri9dj5sJk zmQJT@*tLx~E?KEYs0R^-b<9%gR5`N~2zf`SMyZ41pes`xEXL6NTGXt<7_la&E>z6t z0_8Sx-cJJ=rHXL03a5%#MxBy!+Tx2#1*&oC$c_5BEV^+fc$i~JbxZCjS8*~ixj-7H z68hLU0oqsy4qbTQ1WFR9|1hdv|5x=*M5vdo_e2r0B05A*4gx_59k+uZ=^cKiiu7Wt zJ&8e3lAeQOy1*A~V7BoiC%v5rvs^yRvK0w+(1K!?JB@A#k|8!rsQbTa>X)tdqy)W! z{AhLqEh?Ig5NxrwSvKAORUMv)VPeWXZ8bJs07Rag0EVU`kW?8JYx3zugsg+M)fn}- z@hdhDSO(qJ zX3&9;&0yJT47!gFyBeicg6LSY&?e9cU5s+o2z1dUfP{ENdTdxFPhD3C`+zS-yK31h z#MX4vpi69LP=2g&X!GZgUko~>4a2}G(E^L)(iRa!$L<)~^f{r6(XJYOPQ!NuK1q5k zdv0s9=K>!uKQ;E;#9_z+wj@OW0r|1yxvfc_3%AUst!y>wT%d!}JW5Cf(XqCnNu3MN z7n8NJ)yQ)bgn{T0kfq1k2C_$?AX3G4W0F?38hMU184>xEl?;iGH4b_DdI@+!xopPD zR;SJ>GahF4={_?i*3)LrVHq*$DqD>?M-oWLPKL*B8=ADaSb8zN$uwOpv`qM12IV6O zALS#1x@9}hZB61_=$1`Y*=n8VlN|89HO&8Ipyy>!?=k`v}<`_!PYgdgp z7n?5OlcmRQ8}iWg5`Ir;H!f49Mx2X~6bCRl4+f zB#E&TlsTrY^V(IT%tgQqe6sZ5u$hfR{Hl2zin+~-=1MAfX5=pfk$^07a5j=HsPTyW zGzoJ|D&zwyHL@Jouy5G#%0{qO(q^f0|5qK^b&LzAXcG2_0;Hb zUv@sapzK(7oa&83tEHqz20A`kQDet_s}SfyR>8KJ(~ala==j}MjT)ypzk+#&tH8MU zpl;b{N|PIhN3qk~C^%b<7%zth#}cA-Z9#!Y9G)gIj&X%?al9Ha9>f5>YP2{_(-i6@+A3JvLXCOMhNex6Lw>wtQ6t66c)NO4MD zkoc$#KD_)`THMyA#UVc4^C^WJAUMu49BoRxW6UAaQ6t0!IvjE%=(uABap#)b zM$8e?QKZ8mH_A!Y29l>%(@$H0_#8h!O zGU**7L1Z}vLIj(k;%vuvMzR+aziR3@rvHe_4HaaFKm{I}ymauaCz$St{AB*SgclV0 z<cuFV2>3vcNCjMewV-|tRseWaAeXu79G?@9y)}MY#@qG&oYYWc>gSu z-aI<;&!kzYK8Jc3IDC4RAvDV$MPIl+1DnC8*5biH7pqB}79$?WuQ?qZdn_CoAMgvos3Re>#?_T82a2_9J z_Q1Si<)?y)J;8ZL#3wW1CE$tW7XwZz4dKBdZh>Kl{CA+wSCm0BveUB-q1(74hfIFQ z!K02!f){GDbl5w_{a-bmOHA3gBeUPJ_(1J!X&RDOE4A+*be%x)|(9y(4I*B-PR ziU?em7WaP*e6fo3VnSvYK0`~p5e^o~(q!$w~i`;!R$olK z?8Y+TA^D*X!KPVyh~F`oKVZBp74FJy;UxeH_3|5s+z!sW78DKJ2%j9B?u_8{6jyNG zQTgqJb;QJ^u4oevtQ8(0QuzWPQS~8!EEn!-aN$1B2J?*xN?kQ7+(3?$G)y~nSi^z{ zkBCsF$5k}U$9-x%xdB{4g4A3QmLO{)a(8$Me8Pm|LO;1HoL4bT!z6?h$Vv#=@JgF4 zhmPZdKZNW`TL?`PazO0{NAc7BAB>NY2dtM6pfF)J9qy`8{LNXS*j(|ex>X3hvb~J1GA&NnD8LS4V=Y3H78iPr>11@3iE*M}WOVFyA?R{- z$raJ@dQ~IFX|@4;o5+X;g~u9&+!kJ9*$Kt6qqbZ%UL5h1=r5;ic@P|H6LL>@32;Jo zF@v^fHk!gl2K@_oM4JWRvAnpe&5L87jk#CVr2>$kyw zowMaCvf_xOnBf7g;J+hro+c|Ulp8Z>%T?sX5ibxvkq`_@kF^YKUL4wu8MNgp^5O`6 zDbYeXfbe0BWyW2(Exd$zpu56ln|fTi7d&S1A@L;gKq5JHTb{OwZ2%7?rpX~;Lj=6wNkFuDM`bS^E|kErHt)rDe}}-K{-C&5(>2)v>A}%K z_A)^KS6%5P+H`E$lI!t-zfw`K33gQkJcuui;3bnDd)K)Cs}9I!trD?XhUuwdq)WF% z3|z4*dhBp?|JR*B7^CV~GZCJD#YUuYiDZLmB9Tm@1*eJ(57jv*(`7bjRYu z0YS_k3O31jY~Q#4tJ)F^;NurqZ0EPEU2K2;Y$+>X0+im0YFGTKjgO5WJi&kmo)srQ zaXSU+H!MF|{89Z%CQ(AaxQ8<)d+%W+6K`CS^aOc?q>Qxs$O@w9^lU?{z8H4jlUu*z zq(=kH$PI_3$4)=@WX`*U^oscS)h8Hm742g634Fmbr$?O?QTd5q1J6{$$FDw_>*nQ$ zp?BhT6e5Ns39|Mfv)&~TiuD&GKc(%#Km_K~mmtuC^raXK!$?riK7@v2DiA!m?>k0< zIGfNKjI;@Qg5i$Xh|GJJkYEuZFGHE`jz{QV6$*r*RVXO#2tt|oRzt|ILYeO75sItc zum>j*)r41}Ai1Lmr2|F@gklxO;EN5Tf_pIGqg6;wDZC2V`@}t&_%4A@=s2cM!XxsJ z4T3^`+JpzBRp^0OlqAURBQo)=hA?IoD(xhMkXE5U7+!_!ULv#KY6y8P!?~%*5GPOC zi$w}9v=_5u(LI^}E`d<2LN*2G$*gybl>i^DLV+*j3kB01u@ag8E`d+1!kAJHPi_zA z5n|qWWB)ZlECF6FhhnpkCM|79m?0 zeq6;! zV(2y19dx-Cl?^;*!#SG~C;#>ISJtw7g!?&f6zC*r)Ppumk$4B24-$aMQN2xt* zDnX8*pS}|t&|aT6}`zkXHqwk8x`jbo1S{j$` zM-jc@JJUx$I=+KY?OXA_rn87#iWYn_w(2w(#)of_@8DAn)~X7=F<#Lijz5bAaVfg6 zarox+(KpH45XL(6&G8r^`Z9`eM7f}}vG&AQUg$gJI|$Xr@&4iaDIzkG^NVgHUZ8 zpNM^@yrRJzU!lS9CF-MZoA2OL8`n<6et5is5ba*mV2Lo?OML6R4Pk6!ZKP-WQj9Qv zQ+XN3+9+Sev2UO6AXFR2=goQ_J+HkL_(~0iQQ~LGckro=Yv)Z+M{(||Xb^|CijBkF z~TdXVZ?sV3T z;$!kn^%m=fvkp^hcU`P2FHd&bi(+F!`L(df4m!1N<@Ve3PJdnIL`r;Ne|>tpzmC|g z+<%+e`s>)Ubb=IYS(-O&uup!Zd`E#Ayy3*d)Ye|dfko|$wASe_%Ce@n`|HqOW#(aO z>#tV_Aobmas}ADw>6`Ie8GD%8dhGfG_cB5%nQA^HJcU0nzI@t_P)!^TK1^*r_5~lV z)}l00+Bod8Pj7eGVdKi^!_?Mgm$Qg?p=z+;=&;W|z1?S5gfU0A_1QZdoF$AklP+wm z9d(~&O>g(ve}h+sN4Jb7KT;m=Aq&2swHBgscTJs ztUR`{% zr#JiSYTdA+HMMou7wu`LT52!MwBGKmBQ`4atf{THp72SgU*ZdQkheSQiZ70iw$8d_ z($I>$xeo7~c(s%!ZK1EiX^{lqe`l7)QVYrj{@$)u>6&p9*X4FhI zws9CIegJ(3q1rgEX|;9NHBXIg97f3x=&#p)jeKhBuP+*mY#esjr#Cz7$~3jQR$Gt# zgz^`EX6>BKpiNsoCs_66ShhS3!E*q2Y+;i>*=b*;7@`w2yi=nB*n zw%C_X-v$^5v7bG!9Ol5h^1fps!Xf*8=t;Zjt90wl9=jSiu4=V)*k$}Nx}i0N4ff^J zH}zL5YPB`k6=2d|-g}+G{`&Ii+W=z&w{_T0Xg<*c^E+;nw$fJyezU#fH#NlVqZSZOws@^JOzU9%XP-^PUz7Iyyb+D#RcB46c~5; z^gVdt0cNhhiaoz9&>;bnUvCw7`LDOm;k($EPv1j#sQmiqtR402Z^imcH-n{$n7Wx~A*{1q>cD-TW@KeHbwSC^0rOett&s1(&3Lr=#fIGXd zvhWS$G>cFLSwHLnfJ>MbUL8sQO^kZk=8Q;ZMSXX=DYkab)wrelp zi>>QB<1s#YG1UpBaXDyY1>hM9T_16mPuua`KKtdpw(;WZRV=jv572_Q=gr}-lP{mP z!~69w6kcWL^}X>5K-J%JP%HEo?j62IzUzMrz>0z0)sYQ=Sc^CC@~$^mB$$1ddwFJzy6f4mW3tU=!vuK+}OI-+Q8g|7_k8{|6x7X!yTuH7dH25t%wsin!!E~3hkzei!#KKNRL5;*Mu(ZZ>1 z!M-CNt549S-?qtru6NcR_zIoxjPJM~da(7gukVL%;sXN~KPNngZ)9ISZP#AH7hBiQ zoLBLob~i|+G=GW5tS#`h{@#lZwQjus`ib)@I+QD=K81JcX6&c^?=?C(lf0?D+V<<` z&0~C`Isu4{!2t~S58oAUalguCgFV}NL{VI>;8L%w}z1AL`G-xlA&r{;~F&kvi| z(V;?$Q{!=%GV^NpdyVeh;|GGbw)y%|^G$r>ekHyzyYemZ9qnoGRxTsYz7t->hpP4< zf|Lp z?R>Aa=lkJp_~PiOw#JL1^9dW9{P37Re1xs>*$FKX_!6?*mnk>#4eLhuf`d8!<`398;&FQXt){YWCxwHRb@6nt zbEHq(^{25Lb=2^en(9`HPP8WtINNvev_!$$x<4Q`;+6*_G{uJgDn(@7AgjXPd@nXM zcH=jr;cqontvb24xMF7H8ew$2(Hmddfyi5;tH=2JO|h|{JGzn!o(^Nc|&qanUn_jkXJ zPDB9L6dL#$300o{ba)Of3=Q8N-&G#Kg#~B8Yl;kohkV(?iyGy~ecelUo8ZN!ZR?aP zJX9oC@WRk|yHhUOi!x@cv?($cc!Bo9$auR^F7Tq15AfOoLwpZ4_?7m;ZR5>8`LBN= z@S>ar@Y+KIeWew=Ff`t7lncCg({1jPgMWII&9YKPhu;>D_S`qy$!W~oLgetJAk^-#Zs->KeSS8o6=jET1!g)_m{9be~fXfBM0H-40S$pe5cCKDuXMHy}vUDkT;@H^~L zJiOf+2fCO`S{vghl}_MAFt2UuuqmEw4EOM;HQ5gcI`bg!540$sRC__6Wb3;?=|X14J6oAwAH6I`u^?y_C-nE?-*|uZ7p!XL%yc)#(blVTHt{91GbA} zykE4nzyS}3w*}toJ@Wt(hK7FnuJ+;$qpbn{3XS}Z9N>kap`X4BFWxcyNO&zYq*fu? z%QFG_$91JlKYbTmykoR?zf+2Lc=bm+yiYKK_jdOiaU0H8LO|n|@gn6{e-BTGdxjqh zZ^;F^_}J6h`<7n~_NI+G-+(N^@}_CKc*kgMefKE?&3~*3K*F%_1K~}&i+7B+&Nui@ zM(Bi+hx}NkE$-FP=k3O~cwW3?H21xc_$s{Y1sfO{3IpTKzPIkTUA$v7H@@XfaH75b z)wl(`FfjZuc$0DC9izGLjoQ}&FK0dNaIcK}cHdi!8}AtHZSRy0oL=uRgZ9GE&`;lu z+<3=$r|+%W%lh7c7w#Ev_q|1X@t)D%_Dh^L^es#m9`ct~ZVusK1N$ z<~&)6n%+S719GWTyk)d?y#?J8xY>Kgfg?<%-WUkClFBLGGpaprC5+dv6yppO{USLK zN^E3NP)l}tRM2{P{aN<920Sf?>s8?UN$@>h`s{MQ9e#1W{4SVvgA56e7KFyZ?# zhn%q22;I564dG}1B}9m~lQtqmrDV}zb{fDDC_uP;nytDC;eV~@5X{hs5GTwm>4gAY^I{jE{A7CVr`e9`S}ZAhJ=c@sFr`N zX6YWiu?1mvf;nod+sE_r={ssn2t!gm`qp<1p|tvY*&kjd{-d_K*ZAIz72p#)M}&wg zeQgSqA*mI-Fj9Q~yT!nO7bfCI-}tU;Pp&9}m~!ofLE<~$E$}3kEB@CQAu_xm`11IO z-m2ameG9x5U+jIp`CZi?&UO+XUCxBs?q0c{Z-Tb~RHBJB==Ko(Xfccu-wbcoVjRZ44_?(GHt!w>AZ)`~q-g7VZD`l* z*o7Fstpo+P)*z9d4#3T?_Y2_Ra2N62@CIzLZG8{CrZ{;;pkSM_I>b>M-z&ZOc6bA} z*r>h0{!jSOI@CLnw<&mTB zf!DF&7y@j4o)biGPzU1oV!#MhuxA~WYAPt*ATD;jlsy*(N?R@8ZLp$^Grk*i;Q%`sF4ew2CvH|4S!c58c zy?3zTZ41B_U*0_Wes>ieR4l(9P@oHU1mE^7@oi?{uA>muzZRldd z`c`+0u0xJ1KbN9Ea$0Qo+TUIyY~K`h7k4|n~p%WvsY}g2LFAQVEx4(D1 zP~$hw)P2*t;)9AW{fup4tHiNyfNvl~)n!4LbG3H8S4`u3;k!0ggt3i%4}1e5IvI*EZyz3`ccSB` zLfk!(HiU5``!@InLgWgHFmDgzW?NK{@*VLugz?GScfvOi%4D?oVcsD=;=mh}%=FVN z`q~i2=WpK(uOO5TED&lWmk7gb%lF0G5XPr)-w)qF2rDbX(#qj};#=cw2*XPXN8b|P zK!`|L*f1}QY9D+R%f36_hA_ORaP)2Q4TNADMcC(TqrLDo!uRs7U;jecCcLU3ZwHT? zy~UmsPHsE#FirE7Sy%!d%XfYh~zgK=1b=K*D+UD zTjhoJq=5>S0kr%lS+TG%Li}9#uI~Q2J6w*}_*OIqX&aRX>R1qFc{P&ZKzqxl@9>}? z?608%Vy3JSAP&0A3rezt1~W7vz;^>g0ERq`iGfCVk}ZgZ^Z3QR?GWC3`PQ$0A@D+y z#^u~bc(M@-yquzOhcSfrZn`D#LWagwt44T|$q2kWlfg}t5T1PcuJ?uXj5`A5pNi=j zhj#4N0xx?pQ->~Ogib$w6W#TAsYGwJG4A+9rUnIIJ~fC+cnI(Z#KyJ1#KBwbjIZ!5 z?2_LkL#Zyr_XC3C8pm$6G49YwhNa$@XIMCPgz$bqa9rcq^)ul$@6)4_1V?^D6}v|W z?*|0OHIChCTik08#TV7ykWHB(JR*2+^u?EY2dL#5$8NPJ?%>HJm%z&>xw^0=zI^&- z?Ed!WQMst|BjK@akI3&%s5;1Rvt(&sr;Tr)_AQ^b!;@DwR?g{NwH00{Pv~2wFVE7* zd{q&oOt{XemQUN^$vZi#$Mmk+4le+r@wWh!NY4R=vGGPfd~v=7V3_%J)t-0(5PHV1 zq<#$eFmH-?VN-nhv|WRE`%KNg)vkDf4%?qx@s*e66~rukht(C%PCso&hxdQf&0Fn^ z7x>U!Q+%jFmiWSrl$>x6%#isvYtIAR2255DZuX3|r*g8!d7(FfQVT{9Nsj z7XY#2f?sCjeI)^g9rDf(jqe=X)V>vTO@|J-GbHpp$SE{qzpy*r`JwS0bZXv8Go}U_Ca43D(7x!W?0^fB3ffjt226kT`Q` z>z*&@x{ncJ%R^<@LGRw|pks6@{!LS)8BHg0#fQ_5;ww&-+Am*aFK>6zfiJdhTMu3F z;qz$>#>bnDbhU3-9Pj+pcpSZ|Kk(n; zf7x_br)~4K{@y)#K!5SEt*w==`oke$X=LX761LOjU2N}_4S+8`w>37?<+d>5L-7^y z5jN91KSI94E^6QS;N}O%EA|z9u_UiZ{$CpRj!Z8I~@y6ZNNi)XMva9zw5%8@#WKZ;8pIA zb-rV6@U*ShqW@~2yw!ldCB6lrM|O>~WwtvnRxV%2k8ik~X(N7)2fae}{6(By`*!#) zx_shaXel^6`Nnt)x_I~V&G0>R#amc9ZGW#^-#5iu(8YV7Z-non8?!t1T{slJeA?#u zX4!b>^G)zgbjZ89{DMrk#E17g-wfZ^-OppU^ZoBlaA-w4aN_mW1Gw1heCvA) zoc=mvsktyFd<(n*T%9pMr4gq&IV`L-=Z+8!;3_^>+YJJ`?QtS0xh|Xq?|kEX zAKb4hPUoB5TdGrZr>yzgg|pzxr*FjTuYUn_hhN2oRPBIge{2_ELB`tMUfI_7yj#>4 zhlTHR@1o1!|JP|_dyUT3p7zj*`nsz6YN=Ai`^w!y+uLh^Z->UOejW#|Z*}jf?>J{u z0H^R3=*y>>!?dXHR8b!;3RY6Nn)>qX?4+%2ufFBeHvrx~`_<1wYB^QkfWAV9Oc>Ns zytC^Rz5#vt^bK_4eSKAZ?#!@hUzx1T^So2|67=QMHvrx~`(4a^r|K)vSL(y%7eSX( z%Sl_?YvaBh8^8N`NG&Hn1HPxbB63gK*h|L z6{eqEKF;CGaF)NpC@a?A3l>?stU2M5{~lyrvVK_$u*>>HC%Ptlmg_CogXro9{mp#+ z`SSTL!e#l$^-_M9EuWwK&6ocIbqrG7mkdzVk(BpW(C(Mt{DZ2XI~66VwK`|9+kk8_T^@=#x$t}4ZAp>W!q3QvcnOapRFNgz-I3|~S_cP+$! z!~2fXg=z5#gmh#8h6lwHpoojZB~M{DG!h$t;W$sSUU))&R!>joeP5F#N9#Ly*+s^yQaheGMtkX!I z%j}CQ4PfHZ>P$Z(E_AP=pjsE#*9<`aO%cE`p$!9&0|=23A`nBDEb6lx5Mu+--ZVhQ z3E@3Gc|&$f1Ry~PB0$xt4!Gwuy7;^OpAP3L@)DR$s zckfO`;dldLYyjGu1|UEY7rSH|;~`6o3_x*Q9rYvPV)treymQ3>tUQx~I7C&CfaQ=V zB$a%GkOgAMlEq+G-2nKZ+h7ys+!=EK11RD`#w&{5`{Eo0IrlRCKwNlv?WFqH-*U#9 z0qDOe0{Dj04Fiw^XaGVMh@nds752JDkj4iFFngy6Fn}U1bjhOlzAvscfQd`1c11RD`#?x=Ki*pe`aWp^{-+C1bx+;^dqM1?J8~gn0+?Pc_ei&yr!wkTzs2TxU z{uh%GA|XQ~;VrFGF%Y-`F*JbLn+70fin!1vi|)z3xY7VCai))D$1{D2_|V;oM$5i< zJs+5|O?*-=&-xPYEMPhN3y9d=a{K28@xuO$a0Vc)X^?#7gQs8G-yjRbkO7NX#=1u0 zgSfdH?N5q^I95rVM&ug}8VMP2CC>Db>`#4(_}KmAiFWbM{$w-SKVNO^ZgqCc?XOH$K{OWP~=bHWHYX9;CAri7^Bx1xx#=b^)FfS`%=5CAqUjRj1{ZPCZRxV#RK1z^w8Nh08$wT=X8Mgzo%fM52e z0mz6VE^^7f7$2?2J1Vle;T8s1i=%tdAQ}IMZ_ZdW*uL5#0AJ!ME?;fzZuR^w55T+( zQP`gmz7TNFN>1JWY;z_1zaYaPJRvz3ZDT~hU1vZqcccBuh$1d_w>Ac(IA?znEXC2g zX%Om5#K-O@Pwd;D;+_2|p5pS=hU`}K7wg_$se;ID*@A%CCfu+;BY+SISu_&5XwkUb zfEe1}+$|Zz074x1{?gs_8~X+bt03qe7Te29KO!!4x1#Y`UtL}W$pg?DVJQH0fL8Nc zb`D(z)U)`;05r$~F?30lv9FP^3evOsnW`W?OO28fhMkY)8|~uC09=WqdnI=>eTn$k z-QEaG6k&RuNX{}K);j9}s6)c96VS^*0x@>cHbxAK zFfVsw0Fn_!TA5 zNa&Kq4E6@ZkO9r!$p8ir;z-}6OBQq5`{GIin7CvB(~pP?-AnZm1{_``yITY3b#6P0 z03DRxHUJe)h$L-+7`kLJ8(wIngcuuuJ01;?aRSNM89=_#pphib8NiEk3~2h2)yD2t zWx#N?B?EfzX6+w*KhYQ5YO4%bh^y29dYwDZ?5|_qoAzfloB?Q%1!C-?RR$~}#`dSX z(f%0&Qaer~RR#>jIr}?tFMVhY;H58FZTfC0z8I9Rdpj#0&i)k7;L2AUx?AyL`MNk= zWvT(>8ITUn=l18Br&-vFg!}PN zai#rN;!Gcl{Y_sYK4iOM)PKVu$=gf&oA_jZu25GSyIY;@u)?qg+@XM*?#}fX=`W5T zY_-%gApVFu?Qf7pBe9Ft8L@yE#y^#8_Rric?1J>gYxzq9MB<$NxxLT;)DKn{c0u~$ z-2`=UIRO_2V1%b52!DmXZUC;@3_!>NF?LCj5l2L>5mHi7gj?1p43P-f0E)QKC5x9K zY-l9hfLHEC{fM~Gy^7Z))Wzitm>WRfj3Dcy0hj?)84yFHc@^}fAwUefAbs)ng$;;d z1em>P00IxVSS?yi;ReTxkFkmkeO~ z5pkh=6))SUi_7bPc?9SiHbmhm0?2dqO<3P~kwt((7Kous7H{#`fEXKqoKFKJ5t0|i zoe*8Zegv@W=nOz{G(Z;LyqHxc-NidV>Z_|2;>g-&0ES$hg2(}cNXXDg+zC+``x;?o ztfU~bHw{35A})5xHpZhkX8>+7TwNC5apyxfqW#s?3UOBFP7%P3rXB${CqRgVEE)+} zQe`wij3a>dP6jZ5A}(Y+{YJYuX8?*zt26yTT-^Dn&wLE|4p+Gi)pOfq1lW*CH3GQv zaSvcBBAgI+OH{_bM#9@rz1f=v$T%VHd=&3xsdECwl?E_zG(Z;Lap$8x^I-;%ifCmH zsMoi&WP1R2`i%%62M{7}M~DGpm)y?_6=E0xW^Wqc#R+rV9Vy-$6B{5G=M2CthO2wU z_Z)XRiWkb%#nn#2%v%@%uc?4q7R1zp5nvj+e9Uooq>0thkN6St*+kh%GjUNDa*AZUPfASX_~v2EX6oKv`SC)0VzJuiT)|JJ@k(@xE6qV?$(D?F=P~$66PTSl+D2mNI`c@XqJQ6$6@6wB4araW&tvGPQ`S`hevG^P;msLLyx z#}$gF7DRmdcTT)~+Ae;_)@7s(tSgB}hwqggw5-!Ee#hqAsJVJ-L&PUdxz$%`tdEqB zW39X<3eI#G@PB%|#;U&w0o470)J90u5FjzLc95!6$JiDKt03I*3;GFRP*VaU`G|ud zPsD~v6cfVO@ejs6VNg2)BX-X^ZQpq9k9sptUczfz6jLS#rOupt zP%1n^Eo7|8WYCGOvnDt~-k~;vxaA#ck|;=|;P9odoIB%x$mdqSU?^uL3n?sF4y>UsG?ZnD#$mDDW&O~rD5R=nih^=Jn5nzSt*oN?V*jOG`FLJqUI{Vf)iCnIhZtHTHFj6kpuW}OzC%4Yvxm>MS*5x`D z&IV<$-q^3?k$pXDp{_2M@yYda{kq=Ruj(0dzTRTjS^E|3O}SjZ%8mU>KHXn0+;S<+ z)Yr_IrEKazWHYdwjTFPsSNcZgs zw9fgbEa_)wkcqb4r+jTRbhHG_PB;oIbiDeU%L&M&6sqSp98RSAHk2ewoATsKJue1N z7H~?h6B`1nUUnT;vKY`bLQZU9Q*F>*XJ~{~X|Hs)m29WyV%=f4Y$$&Iv^+28Rjks{ zR&p84Tr+hb_6^%+Lw&BL(Dja88OW5&^{dt~{p z)^GaI^(W_Z>z7B`S@)4*$D<6dS4cJDX?W+Nleb9r|5; zJY^+Y(4ok_Y|G}#VWD>{rZVv#|Iee)I*-c@p`MnAdkF`^Td$z z9&a={4D!f(7`8c3f88ZZ;%%Bm{DF5!9fmpw)@)H8BvwA@xR6&Dln=t8qCtz#@9CjD zPyjxf5p+2UMApYkYffIBd~bTEQumQP%enQNOG4#v&XibSRw1L^2Hpmh) zWF(;t>WA+<4v?6xXQ>90m)6JJPm&LiQ7EsC`zn(R1FtVx95C{#T*Mnkj03|g7`&24T5?c9{)`S#%Q4R4qdGb%NwMuz}f}HXuTk7y4y=$fndu$VDbE*}ycU z@LI?w7}~&Wo@`(URbH4Y>Q}aHkj|=V^U?+;FWJB}r1C=UtB=ksMZukOG4Ds3 z4H$X#I3O1g<7*ZHHVFN)K5tWEjBP;vrwxcubrN!)4yeR7&_XG7lH@rXP#)Kp=fbQS zJ0GiaUoNk9PUfvhvjHQoZUb@wF%q-rgq3cGQ7p5s6ZmGpMZ9Xt+>{p-H8*8t*~zPO zU!jxO2HwZ=IA^8O$*Xf;E>CT}KBFN$TMiw`24@~Tt6X&Er9g6#=}2u5`Q;*H-v(%C zMK++#mpFJK6nQMD6*^hI(k{=%0p%?|_R^5bi@0x5$-cZwt>SFsCQDv))*mm?GI=U` z`WoGtZxt~c5VK;8_;L}_fHAVc%jUE}&JBD+jk7`hoSge5Ya@jXUh-&zJkD7-aq?u< z@%mswX}rJclJUNLwqLcO9TmHXtt$BQc9k z_znYyvD)k|F@`oUo6`n4Pw=?uMuM_XGO@l)4Sb1&jk?h7CHVxus6(6DB3bkc-Ub$p(f{ z<%RoleX=B!SH^+KOY1WYsl3p~>Jue}yh=ak%&YBY1MaYO8yH(@{cdpr$!ry4$d@W) zKMs&XuFnc*mSxFBd>7RsFWi&$EA8@}4V;gehGc!*ES!(kxleM`oP)B9t7pFEin{03 z?_e*cKMOje&tktL8M`&hS7IIKa-43x9C8}hm&Y>+(8;ZHTrRh` zIQ6=4lgcCt>tpx=6bn)u_uGw$*t#m`Fe{BQfs-q zq?hE{`c-b|NA(GlV!g$ssI^@BE9G+iDmUb_`gBPlx0tY4%XRq#2Rf897sXEUR__vOs+2$rv{%HUF;%w@9!S$*&2(GZ_vHZ~x*Zzg~ z;?7e?Wjp;=c}Z&5GVFYo!DyV(Veqgn~iA$lD5hVgGLw8F0ZtK$)gQ&ver2ixTss7sAOMW?SR8SYJSi3#&lp* zLme>jB1Uf9s*~6+tI22KCy0q4x#nvQnOw8yr5h#38k|lxiQM9j@|+EvkC}#Kef;SU zCvTG|O2eIoDP$tet6C#sRx}8#W*p5F;^*PI%ScVQk||B+Wq_n9XT}oEvzv z*U4++KFTX?VDi%XOhdZ9(8o5WXmT4=F07r|*)$vQC|2bn_9htzhFLMje%Z#CXi!8p zpv`FmB2;_O_I(co}SGlluW^kDe7m@L#=4DLD? zxnYBsjud0)mnvl62E0I4=0#@nWCKGe@_380^h^CpyS&l{CNIT-X-MUTKBg+!msdMr z*|Qoge7 zm$^J=1MW9mAGt~8#XeT&KIC4On^xY2jQ8&WZgwu_?SeWN*~NWgBxc1J^5w=rOMx-m zlX)+L=L18i@0_#r*yg#sycwI-m)2_8lvUXDD=H_$sft(BD z=94a>K_{^fkXLAfoD12rck8Qip}yINXOBED%;S*BG~>`lHLCj&??^EYO-JIF><^Y0 zRY<6l5@Q^Pw0W`t?}X|8$D+2ASM8sK@=7k`9XIBoFO4j5Xd09C#{HD~FiR-k#Ua<5 ze4tn_8w+l|)%su|ua=j(VV1L9$*=nW{gQr4%&L<_91dfZ8%vC#517x<2gr>oFXY1d zFiR+}^nsN;7ni0nl^=4WDskWEc`35=0h3Q3$k)rFz0+@%=a$P^m9x2xq$CdJ?Yk-m zqqw%DoQ!|ND!O2C* zx*Yxc>)e0jt--{#<7*VAlJkr<+Ce`cR>cGKzX!5p67)>O*HCc%l`WE{Uy5}F!D_p=J(8^YWvG*;~T_C z%whu;YMf5C`HwmYxn^0QRL5uoB2;;?Uv6`nm*;HYeJqb_7G2!>wz-Y+YTUy@0d2tB zrFEXS;RM=%m{lhsFV+WC3cm!_^?B=M^JD`$a|0*$olfd`o%kgEGORm{ zCXY7AuxUX{VA9?GUT*LxXJq~zZIpv%$9Z4K88Fv`l z_%b~Ak7n}}2Zm7Ph1{oK*|$06IU8`lp$+mlXF^)8Z)ezqaaXycG1_=ZiUS*csKxALKt5(ldo{ zI%(rR%5ygGK9fOjTJ zE;5^^I531FkDG(jNgMZ3UTFi9m*T)Qr1C-^+Zbk{4fH-jTn8FiTs9jp^6GIwE+9tM zg%o4Rmnvl228uD{BC|PdK!hqUp0fe>8?KMsr1D}P6BPUFtDTeWUBrot zSlr%-17oXYKQJ8$Mmmx6%YD8S8^q^imiNg;hEV0jekq!2&`E3q?_+tK(`DWI_PI~i zr?w7ibS&sI4tD29MwaBsMW!RwN$8gmY5<<-u~Zk}Y^ohKRX-aKQKm=$B_m-Sy=v08=r3D2$Neqc7I4RUTET&ENH zN`p?qI52r>eWoE@U+iQ1d7rGWc22fwiNr-Vtx??%Y_@A=Z3IHGS+{IrLZ~Wm2u#G%rqqH3;Q0Uu6p0*wR5tYCmFe@`hH;d=4k`!NHKxAdll8emdv_W!x<-WM5qKjyk=WM|JhU?4YJnoOEN@97aNi9FG zmhz$VYXE$-DT%ZXye1TC{WH69Pi(|0{=o~ExkQ|I}fANNq|zr<3= zak}QdlH@Ri8Q-QMU0=v~`pAavQ(PB!07kz)o0Xtmi}i-wSDy_j)~h)HV`(jy`*@OT z>zDP$eUkc&NFi7Ey}Dd?{<8I}+>qDmvm%Au;zbg#^>Te&ugVGetUm9da*#jt`57;} zZ#CNd3FC?0MLqaC;3Mh7{Ebg2zS!UDxu3*3^P7^xENWjvW&%E{Lk_c>Jcn{s+xTX2rFX@+EHl5#zFvv~*N%Xu^Iaj1|@ ze0qK?t<4l^gO} zeU_t;TgH{irJQ1YAwTKh%cdTyab?p9j4NZ0RX##IS?)K4GaaM;(M>!-9NF{^?Mft~ z-?&$y!yo%{$~ZDPTu;VZ0~_ehPsNpEIa@FO$zB*X+U@$JXEx)rc;b2&T(5mg*B|?r zP(X;we*G1>p8hf<>f$J@Bi`}PC4bo~a+2Pef=I4Cu$f)zL%-54*M1pLYUSLQU+=D= zT>2I5lJVQXroF>ay_f9QFTeWRx_;&>2}h}q1j^y4@>qzYQ6G$Z;-G%SE^Jgf9P&tP z7n7O%0WWLPUn(qK#NMC;7a_z*eu5UCqrqnb9**v+;%(CMIHuXqlTU!L!3vV$wevI+nhe$S{QonrVsha^ebq zp=MOj?efY*&E%!PF^#B$guz-LC@I#LA3F0(Z+1mLZRR{pyf!y;bLxP>%;3&9H+G<$ z4eDf``vo@iAW1eGQ+`fh>~J}Gb>fcXl{PSWv;j9Su>rd)PF|g?b9uD`Ax~E3_m)1T z#C2TRfN?;K+~^f!GLXYqC-nkjYy;YyHXuTk7yG4rx*rE@WI7vA9@ocBOyz|eh#njF z<<%OCHr$fu!x$LXZbeUUt811ImL5xv~>(wNE* zxlkWzUvE*!TKjm;g{;Dq>#Yx<=r6R<2P%2l#OjUt0GXy5hrI8ZngK7&s*{ilRmi?h z!n|xUpG$FQBP=Q}^tt)~N~x1*KYGbaK5iNkd3+0ovq7B;bDvXRONIJ84p}iW8_)~u zHsJn4j5!zb)bB9Xxv;<(#-Z7qHpsa!{FRzXuj%gFJj~0cl|0kP5{ITSwMp!A@@c#L zG7e2X^;@hr_Bk=hzPvmyEPQ~Gp7ujltLj{A!vVyYb1{#!&Id$D1IE|~=yS;jc;{I2 zLLTo*dG&#mA`Y2{mR#%xV5XNwk`GX0;sZKrzsia9k@odEAK-eEe#`ZSK3D%Wlw!Sk zUYh%W4ZzR`m;qFIKqnm}H>VE}v+5-D)A}!{lo&%FFrT9jWIksxR;_ zl4lxOe84n@_1cdRI{9t9NBN}>n0)#`vEBs6$*bmodeKO@cB3{m*{B75fEKO$0ONod zvordcNwdRP<;D_Y>;vRN`T!BC4Pu{@uY_?J$}4%m`?zUHk}0F@@hE|Z>@7b zX2rD5#Vk`Y4h^$n4D&=4(tt6x0d1aaUxB5%gb}3 z?o~IQSY$ODF!HK#z%r@XfS46y`iIg_R7eBH&<196+JFc}9yco|ugZl*9K<=1$)gRJ zsjIx$$Ew^{$jft*!Ul}=#6>n7QnvxQfEaTw3L8+9&MNHNfH^{OVQ4a&(+0^EB`2{T zoN_j3<37qOV=|qI=+kktd zvjJ_MY+wjgUg($lm3Dc~29!q|P(vy&!fZgyiZSHN zoq-j@7;=%>oHigrl^6Qu&hUwsSK7ejB^#KAL>~WT>0|XDGby;JatVI2(FfXqkyp0? zxquk6GqU|&jKkQ*m*KGZWHzS_h){JB`=xwkKMp9**?{{E*Ox>n_i(5aerlJV8t-THlWRE zgPa@UUdhhziI?YWKzX!59_PGo?rdJ?KJ-WM#<%K4+sVcfsQ+CavVj5OBVN^i?340o z90LhoOJQJ-p6)&sPkPe$LZV4F%2aD%40n4lk5zlaL+00AuB)5*T*-dm+RXZ zJP|wV+SV7?Ck6(&&VCxC%5`*Y=3RzY>>T$`RLH)aiwmHW`BSp9AyYY_KkW>pP~XMH z+n1c=x26%56LQ#uHLpaZ0VuYivtW-=-JA7xzk3NL}CDXUj8&*_Hav z`7G{tDC_LYDdTD-hjB*z=e!p8J$43CxV|#3OfKaV>x=zqo1eta)wp8R($2;ntNRbc zlk+7nkGVMC<}t3@#SwXmb|n(=pSXvyGjKvVE{-UN>!AizPFh^Ko{fPMxg?6q!Sai( z%Vp54znjRkIabe+i$W~Z_zvQ!%=ExJC~wY->>D^?ng>yCQlYS{?hZzZ6NJMuY1M1u0-T+T~!W!|I%|d zxiZ;&$U?C5Y9oE!&|aMKwLArzSiym<6Rf;N14dXimh$GJo2Hq#JDWl$Q+W!Md~#vr zT_J}__&Rh*B_P8T9#Tw8l$TvJ9C`Dmlvkgk3FSG*C+Slj3qlNpZ1b_#f;#zC@(tye zo4lY{#%ZANe!ef}lFT*#lO)6yH2ja^;=3}%; zPDZ=|;B-$N`C)RRDR8@d=L0Ur?9#o+V`sJ0Z+*C=(67n~e1MrW6H(sQsq?^VJR|9Zm=$Ak z2#2wa8`)SZlOyxFR>LlcWl{_Y2$>&XDDnIl=RpP!6aJ{7un0(61 zV~nq~bNX%Ly}Xf_@Bh3ZWj;V&sPh2hkQnp*nO#1Iv5gy%EC$6h^EvuJ?sM!gIeGGx zeH&0-xfiVDnMM|$H;su8u)E{rxA7k3J0BqTQ@_P}W1ka~?5{U(WEMU^rb~J0YqTUC z{QX`q+o%LF60_N; z;N^4lf!yaf>gs$zzS1tQ@PV~F(}?oGOJlNLRydvfHr`{AxA1|NeELAXURIW!Jkff* zUgSLCSund)GV@ko+6!hL?yC8~Y=alXnD1At8#|13ZY(gyK0u$N59B__E(wSv}ROowQR*bP8nTg?WH3&j}nrT*l~z73cwI~$}wnJ)IshEU}3Fje}cex+TW zvjOGN2Go$s3w^9UY*NH|o}1@3VDX3hfsHm)A?$1}Uulr3}@aF zCl|d&4{B#b8`mI4>PT$Bq{3mW*9-H!ftUVD8<@?L4Gf{m3;j~RvTp;*D{Wx%()vt8 zDlhc0o#7JNpwa-H`O^|+14dpw4#)+>nAJ`uCQZ%;qN#nIpezyW2WE5HfCxn=+@PJj zHtwT5X9MPIF!HMVfsJbrV{*j>W9*k=nSGt`-0EyVo6`nFs5*)LvW@#F z&)I0A{ZfVO>jb<4CZ+JNk-@n4gp5 zp32TZieZdxK>nuxR3Ij4ZM%#aUSQ%zlCW5{Sx+{cxY_ za(PbY-lxb#Dlg=^jX{>J0h42XnS-oG=cjcu=P!Z1F8BzrxN)L?G;{`eCA_{R$~icR zJvn$j-7)bdUFmT8m2DYL!CnRrGs&S$6S}VCT~4MvvMsZ`m|-&qTo;{|GV>&cx5b=H zd1PB=F%k6|1fWcpSa6TfL)@Ia%*m8Twq+L6QEQnbD6W(1R^28;IGOUuwoFYXNeFA1 zrJv6B<2jJIT(SC%o64E%Kbb;5waFkP4CPM;tmQLcxn}A><;VV1B`E*X3B^BFjNn=RaG;LJ_9}=6O;N=5}49E%YWb-^4adx7z#l8+}_`*C5aPRR1=gFM%Cc5 zAQamt-87kW1^5ls{cB)L^$^B^3W;@3{H;Hsam=R4W}Dhc;8|WY@UPJ~-88c0DcCUk z&_7xQE3aPw=6c3wGZZP~vzZAaH&c1q$nq(NS?_|Z2wneZ1Ew(JGc8eGPF%c7-~v{@ z(k`zgPLsz#;RYr;=hX-&uTIubw&`4xtDp+cxoakS%-%9TpqnQhF!v&n8fL{92Xmdw z3yiT1xPehVgGl9tL8FV=Nq)bYq?x_n z+mrz@60_)pZDfbBo~Y(F5aI-(xTL~tP8*QwR9?7g)F)Z=r(MVelvmoo%(N{I7`uD1}!lS4Rr$XDKz>Fhz7TqiZ9 zGGo81^P~5Hc;A4k=-spjqq1%fV>CGgB&VR?u(a)TBSPAAgTJ@hzwsG;fc79t(G5$x zPG&ntQl_&9Wzrthl*)|#uF8>mvzIwTymy=qytcUP9r$N*SJf80M7ZqXnVuA5=%+i= zFNQI;18tw|Uu5)7Z4*ci%xjJa~P|6WZ@^g=~UXl zY)%{G+`uw`lP6zk&`HQeCNHheG^FbbeQam?g*K0GR>200JlcSfSLLE|G9il##Tfgg z2-(*O3d2EeWRo4V0THUajm0pHv~S z{taKOd)k`1&pF{3`=flNO;+jqHa$XF9Kp~b6in!B8)GdBCJZPG-sI#qS|M`CG^KGj z##wSXWiYMea6M^#rV*8!{-u*EpKd5St*;EEm0Sibdsw35qUrChFG<;H$gC#`(FW$>)zGEga(>le8+wzIE1vcFzU98#Q| z4lB6~TFT}6Rc`E8b+XFWtJje0avf2z&!CA})aU^8I#K0v^;ShyE~iSEfZ7O(t~d0n zjggaFu3UfD0%Bb@Q#IVHzGDhM*O&6<~19W;JR~O<4-p@ zowxCt=zN>k=*qV4JU8Qe*`4e*UK82dyk?^kTzAfEaYI2LY18>Oui4-TW#_yWHy8Ag zHrd;}W+Ne#o%0&s1MYO*mZyo%w|R{rZ1&E1jkoli>^5E#+1tEklM-Av^Hqs4-s3D~ z*C#^ccDG!X)j}bf2|K(-`mlB9ycRbW>XRZX+1tGKI^ixklj%X$9XAa0k$qp2bys*T z>Dbn-vO*ryE6K5}t#P0(ubkP?M5CT??zJSVqGaG_lJ5g0h*o?Hew4O6T?MS{a2kA#Nt zI0jTvTi4HkCytExpgl-jC6l@hwBu;h2ZNtDs2@1$kyo>vKB^?>e*nkEZ(x_z5A$TV z)aer>17e|$6iYIc!%`*19`mqj5Ajd&ONOE?Dz$FgE%r?J@Wd3vxz*iua78>cVdOSK`hjf=!91%92R-xhhf>#3;BXr zs3*k|`)ZwEbNfVkDGxZS*meBMTu(WKZDXgGI=^z1Z`&5H8I>jJ%BFx|i_kweCPFB4 zTj$K>$d$}1mvttClkrr-MFHm0yg+%I#(7ekc#CD;+paW2&w`&C0aXd!5rnp61Hjn) zahd5w{Eb~ZhoQ=4vd0bJuzttK9cS4jkID@9*^S>Hk&gFm$iHuRDdYGWENOjP-DFgfy8 zlLi~xJPuPm!33BWrlu;HZTpTywJz$BOW@oN*m;(N5@09 zDcNoPWCR|4>VaT4qY085go2D6vrd@ELUuL;Gdhhx`c!$bW7f$plIM)T4Tthbeq1lf zO67-Ss5c+uK#b%Ur>Wy=y)H0qN=D?f_Q(BB=UhwzxL?nA_-O1`cJQeT^T_@nz74T&jG{R7Du|w7gQNb;NAT1o} zU&?V!{=4ks9gX-CG1|b3%8ebdo)vnza151OI>Sn)BSTirgbaWhwBySk$NGRr?=h4_ zw35xhr&DA^h@01;>^c#8*(C{i*{qt81Z^}%*Bv^fKCbKTT~$`1Xl zKG3n!c}YGi*$nn1+w`EaL!YY;cNDUdkJIOOeBA~Bh=V#298Aqi608q>tZ<0$OT2CS zJ}Zj!fqc*f_2bC@>Vq9C4BNhcV$#F)XM~7)S17xl^yR)UcG&U#XC$e{Z-*FwQyUKOU0-VpuBfBv_;PlZ~WOJQdx2%)XmQGfk z+xQZS==9NLhY!|jx%crRdVTL>WtF~Qe4#i)KU5*HPF6TmLRhqMj<(;Xea}#;%+UYz zEAPoH4Lm8fDmRuGV;hkFXagctd9hzs`7o5{Y(ROmL0Xb>AcMl$pvr-TygVTHkEiObSG>F4k<<1ggYy;Xn*}xF0ypRL+EBkSWmHn`A@2ko=8nMayj%HeudPRLEV>R1l)6?xOKycJpBm|W{5*knZF?AO$W@kTkc z|7s*FuF%gmW=Mc%-8#2Mm341~4Y>k2mriIN_z(cyhlQ#w!Py zj;6A-aoIqN%1FDIlTjs9xx=h7%1vt}gJeRd%*m9O+nvlRnVQUU!&=ECu~24Cq~Vaz zQ5%HYx-2)Vl}tyXHn5|*je}1x5>-yZbpWP!%K4a@uc_BgTBe6V?r>-$S z%;n_e=1-3H&vuww>7Q{!eE9~#iT@hE5iI)8Ef8guC|m#?3#X&&b6pR4O9r5PX4A7CH6 z_YW&wHFxxnZm%DSRBip#xBmUmzv^=%@-r^4_^+>tF-)5lGDDxM|7?lL+}@8#)zqWu zRo5B%QT=C2Oy)M1zp|U92x33iqB7H>OW&%`fSAk*pC?_~Izf3(+rvLK&ml-VnPgug<&+s>2{1JZ=f@7De| z{rm5}`V-!HhTr82Lp`Uuj2;e5pp3P8ZM zJ6;cu^XYaP$IEbeT&7N+R4(#Q_VaKZ?#JPFyPwf2pE19B!b{?hr)fT;rnbn`6Ow=O z2CTJQy~?Z)SM>iTi&PvfnhRi<*0fAZc#L*w&to6l3<-SBhX$MJD`;tw7U zm!VtG>PGLM9PXFX?Q$I-&+F9Rrt5V+J+I^OcD+7tmvOqPOmz(Z3+OD4i9}&UBo}xji+9I6a9SZ&;9)9FWqoEJo?A=GG52~ebG?zkt&$qsNTrQUpbTXqM zdpb;T5PdTAq33bD9WUK^ykqkC*uw#a9u5Q_z(1c(lRg>x5X^BOaQkr>x)FZ+xSa8y z%c&bqmuvr+#c%Ppun6r4gipBct-l}p=k+xA_Y;ozaN^;L{?PJd=tJk}h_63y3m20OylD^9?&CGnV}Dz@3(t*yo}H3^5}=lG!Mi1aXn4a@jlMalRg>x(6ht0 z9+%UM@=t#pPS^AE`Mh7RhiS$lj*EsuAG$yj=gV|IqjuC!^Ef}}@%HSy>+{%M5p60n z^r887ntIV?H_g}4 z;c}EGm#YHahot&~H0}0yo=&)-HC>;VVe0SGb?m1LW^nY$(1$MLW$w=R1B{NT;_G92 zJQ1vq@pihLPxo0oK3xs)GH(xP_CDU9N9bkh#_0ivnvXc|A5n}rUi8V(hd@*#EQrv6 z%;#{v9*4s?-Y>(5-!MTO>64)kfhe!f)AWEL=YBe!=i&Z5U;%hV)ai6Pst+v}Qa#$! z>G7P;_vthsQcvS)zTXZ5Ncwg;!Lzz$ouLn15t>5F7iguwJ*O*{dG5h$eRsZIA2&rC z`_LfXc?MS>z$OPU*A>G_=gVX4X1pJGS!d`&XL)zT33fj}!9?RYch>;~jfA^LYSS(2 z41EY>fY9oO<9)dHm*?q< zm?x}MpA3EI-rbP>+z&TV3Swn~y83yBc*@xdANczPuBx{w13_o(>#NDpU)mndpl3h`(}4T zG{cOS`{R1Y_Jq#S;Xd5@$F#@{c?d!Ae2fEF4?c7|UoN-HbG%N+`}1}i9@C&23VrAS z{eZom;6J?KZbmF)`-dakpTiC8pd7#4hVy7)1*xIlraK~gm`|YlTQ6In?1HyRxo^44 z<)1v>PW^M7kzs%}p_Ta#KR~8%eLgS8`~9hCLm#?eUzouK&rY%!Y%$Nr^@eTgdc=-6 zUC#34(w<+*?8e(TKJUZyxP!;K`EtLW&v3rW6{a0@oy$uo{gc?M`_m1F%=6(6(i@No z-6e^_b}Yqm)$MX$$v=r%yB@53K|~Ap9WU2m>bmCxo7#+c)A(F&U->7odtTw|M`RTp z_Oj=ExF9zfkK?t+yZ9GQSlaad$qBl|)^fsQQ1U!pI(W|EK0&$1GrUAK6#CFSJP=k; z&;i`sk52^I^>Te6(RiL7%>T>=id`6XHWlSKL2&pCyCG?S&H{KF;U)j-4No zjm;hKzvQl zQG5-%;PZOJDQg(vO27iYPz{AV)Zw7;n9h&x{JbLdLK=s|7*4*o`#d0#S~L{;(1bmA zICc09i|5=wFUW;XIREyNp!RYS5t&Qde1&!#j@=Un4|qX;K8`r3;E+GxaC*Tx;HciW zbWi=06I^H>d5@$F*?gPv+4>*`(w?2b& zkpN6flr04y|K#a(zCMS??Q*2z^@u72GF@auLc9IAF!j=U@=uBu z4mUXb181ah9FW_NNZ&!EvN`s;&ZUXupS)jZgfZTvcD><<{Xq8iydgowdGdTkwyNiq zrTyif#3}cFzFlt*$dR3QdYoa;3z7}ogMgw$w@YKhKRMnmaP%wEpb6A>k(2AAL+X!r z9v-g4RZo#i6Usk{)bQ3%w-H40gx|vJa0+;I7kE8vv&72M$MsJlPkZ1bhb$Ke!C+VD z8eaDRf8P(-Ar){DjgIJ2}GidQf{V4GJ%FdSaWtW*IgbPCQ)e6ymnTCWx*Xta z6HXhb4I?^H*y^v)5RQV9;!e8GrNQB4!pFPwaY9XSMt~e-Cqk$UkL%FO&Zr!}6zKhv z2h`5SVFD!}8%LQ4Ne!rT>T&8t-Dl8PS<3AG$=e;f^$Zs1<^e8rmUD;f_zx5VQ0P%M zS&HZWN$g((iY9jy&hX6{1<5DMlLwq#u_fJai_GwBkD?^95;-No`%r71ANSjceO!tf zLWSZ9%k2y=6EtwVq2?v=(O<4Jjv64LsXOBjp22JSWavYaFHKJ8nBUo;)e@HFi=;M9DQN1r3~%S9`0upiBT2Qs_~2h*gS%y zk=Q)pd$(nsp$|bD!}at)5fxQf96ssZkh&x86~xdrUnle$xrH z0VK*tBr7NIudH+F-+HtiN-ij#9v>63RIGBoBbmd_GNX=%eM8YMuj}zoV%%#uA>RW= zqRyN;b`}`P@-CnV^ z;hN;NUu1?n^ng-Pp}Aoj#P*4V6D)f}vT;Nu^)b#8D>#Kl8baw3ZIT&Wglg&y?EvJ& zCzL*h^La#8FIGV%FnUr{IG*oUq+W<@)WODag5P2*$JQ|2#BWi>jh@758ueaONO0=x zVIrIb55w&Qn_|;P%U+)hb82i^I1IrqXD}B?`|?CJ9R)G$^f)CQ7u|+FbOtG)+Jf{8 zObcE?6&A;x5xtY)fCNIHjO)}e3HD8#2vJe(XH-zmcQ9x_;Zx+)56P)hAJ%*P8Rc-$ zju0PmO%$y!_X(9_Y~A8SnqlDF5^1PAoh3D$j>w}C(G$*Suz!aVQ-|&GCXt=`s9vT} z!5y0{PMr8XZB(=73pU{ib!X%(I2`Dcp$}oZy5HbL=yIWVB_|yi=!*6pwtggUYR|Y% zJ)ly7S~Z*xhD9cIK}K>L?nnp03usBG%+QBKp9fSxpB=1lIz#*!<+}NVx+d!F+KW6b zzo_Kb3G%qjpn-#g^mM;pB!5H^<8(k%a=NGwEkpDENhy3xs4Ah;f!*(U!h&xo6J61m z>Y()nZRkTMa3mrbg&q7>Ia&!5>K{l{I#30wAbMUwnV9+nB zeTVDgiVl%zX!!*tk9I^Be#6#+L(&5xo@jvf!x{c~!Ke5{pA3BnNk2+_Quctqq2Gau z396Ied#N!TmdK9l)JRMZfClEdKcqN0f%B2dAIHn{j;N9S6CoI_Q*!?+PMbKsA~sO5 zc^t6kKOs$$)TE)1hhRK(8Q|kbsorDfmDK2pbQ6{D+dMDp41EaAPG}a{Om{)mVnFZq zjxrXyynT1afk*u|u2Y|`N9>fhYcD;g6WXN*aR!u(kVQ()sp|}R=z!wE>3Rdh504&4 z5Y*nVUYKP>?Poc4U^k64gd)K6EQc%fm9g*RXfOL2Hgf!*Gb(u^GpYD5x968{HQ#9lU{1`olYcl7B%+frGAK|{3A1bS*WsbL|x zxliZwfIRGpHpc}ipn5`Fr$)OA&7TR?R4_GcA!n6wLV5vXfl8NkhCYO|7YewN2W!>j zcAatZKu`Q|MKcO{nW7DS=yZIdrgWZ=Wgw+M3E_yY63QnCKBU`|RJak};X0*7e1x;0 zuZAoW8^eI}%Wc3>1ZdBThQhrJEQJ0Ost0E&5Q3*si#y!Wok>4 z_eOFv-H=lpknugxm%(j`>9WWSeF$uOM_cUt!1)bZ3qs|3I!f0Uoebpna#}_`FIp!y zOr)nsrLYZRlR*0$CPA4SrwE)0531W`($n)0s!w1{loJiLsmBNE4?1P)H$fv-6 z8f9^v`gp$`5mLz9zy&y~f*Meaf-`scD@d|cX6Qr6wU8S=j%cpo4g^wU{DR{d4K&ER zN!?qY41EZCy-ON}3o*z@=Hq-qT}m{D;?Ehq0DUs_p$R=!^!#xX2K@(kD~d{JOpd7K z-OuNfp0iOwiG1jWW9uCiRP3I2Cq9H_kE zHU+9oJ!*$&x(V92PK|ULn>_qT2oKZ_vtnC>`=Y;uEnGDe*Quoq31#3=C+BQDD-nX* zPw4cHBRbV8GxQCR)776toNh8u)Bq->Z-B8X)$p^|D z1d%=&`jE6zPER>iAtupY#-@a>(ug7)j{7L(>XRW4VUM0rA$owJapr~N<7NS#KO(Un zZo@&dx42G?>rl8cH-OmDnK?pPC%JQlBLF&@xSFW364$8_OW@Zt>eJ#0s0X0yc|745 zi$ddV8dPRnr$&wXf@(NA->5raKf2(!0XLY>NX_B)s-d_}eTR>s{)&DVb_g6jk%8e{ zeM9@|40h7H0w_R6tO<6;?un{=k4iqM2Rg&*8_h&d`TIKz9_S zkweLiA(Xpu*1^R&w0~zrbb2z}%Wz0OkH}!)ZcpKO+zLcyB!_fdM?w=`WX5%B9HOPI zD@6dDrI2d&C?Y>ENW>1<619(nLwID*4l9I=9{qAug>Z63E`CC+;iQZnns#e(l`?wr zc{mO@YM}p&vpu#zv z>XRW4p(KY?7#9!G+(WlkO3m2+ffq*(+^f+{YPlrsy$=^daKmasjNuS^L?0UGRWw{g zZK!JK9TzZnv`(}DQ9qNb7|1ZtXU1I`Tzta)6C_h8Q)^`NzI1sP7Do$23$8WbaNbK1 z4!0BJ5{cG9656;Id!#p!#`{D~w2i zq)3RDaU=i4xdjCj-32RkY9!*=I3G|EPINE>TK|Z1Ov|IPfLpODGxVV=3J7THV6#Q} z6Wo9lHBBCFu|KO>BA zqZv*o`zPvU@B_J;EmsfpT(-PQ+ePSd zetB`Z7SADw-+ufoyKMinzy9sVkADB>Z~p$%SKof~VVCJY|Iwdt&;J+S{KJ?3@{^B$ z#_jsweJl6uKmYN+e)`9+zW?d>Uw(~y^)d?ZH=q7dF5eg5Km6q5f8s9xw_kt#m%snw z>+iq({KxGK~U78;Zw;$GqM734vAUw!;VL!^Ct zC9(7IuMJYtS45cnPqEnl{___a(aSo2`suqbzWE+N{;Ql}Pa^ZSkA!>j8 z(@)?27HawC53<^ye*dShzrwoz!%zN$Exi!KdZ;HBp3mq(Cf8>bhJHUA6Oo_-H!LI@PLSHWmI}aT! zbTUJaOE{qt2f|}v~xtb!;6M6)) zE1z2Ib?3X_$>A(3ODOY}WtOG7JQeWV&QmjOHa*ReNQg2##Pc$e)9i`R8H#?lrDwzM z=C4`lDGs@Z4tqE<3--x2N1H?fet{0nQkTb{NI+f^N(1p!U|X5j16zq+5Q!!dcE8h8 z;4CkIU7E{hwvIvwn@BixLM|=yxtt}0X~Beyx>tz|RUwL9&H`6?9882Lb9=zq4srX7 z3G~)D!r}Iq&k$$6A9g4u&isLKL@(Dc^Vo*h>n{WE1T&ITGwiyYLfBBxF^mF-Pwwyb z5vV7&#GHJJyMMW}d>DaRR1=9F?s8{gp$n2n@XpXKo2$@O?(*crel;~CUQbDlM^s0A zJPmJ3mD4hzr0K}C={EFrU{hMU1I`lsk|C?T%rh2oVlo1!Epm&hFk0aK@U4;|DllKz zbi2crtin}+k?igw51gW8kKa{P?)DE0ZY-@RaiXPw`O=}MX5^*HeG&;kD68U2x(9}9vbJ4t|&}IqoaGU#0e{z;!Img zZu{`?75#nw^6&>b(kL7VM);!$7c5VL6U%^L;v6$GXE74y82S$dE|HT8jS-#|nv}Q9 z<Kk z6W7+<&^H5ir)8iUJg?fM2i9)UXc&+fCdn)UT6B2$3~I-Y#;{dqnKR$*hXrA9jJ5TF^A1m1E_o<}jZ(T;%phJov{QSW6ct!lp1c9=j#4%uvs!vg=v<0nwA+fOJrTe z_XZ!?dAc3E2$>*?Hn=(RY{?lJDSEYk29~l?(?v&^HBCm?wOmpGE3ArA^x?2^*X6F! zWC++vXnX=QluHl^d#VPXjMl_M2P1TpUxEzMFuyyWs5X3ae3%|O2DtI?_l*1f0c$)5 zLX&}s#O67`vhgT>srekpaC)3Rug9Gadjb!H6z>oM&OW>jyY0|ag6Adpe1OV_(Thrg zT8updy*u`ghO_-Qz-Qo4LH`}PZrE)Hf{9qTZB%?{`11k5rt9PQXz=p^NEST5_uDlL zybotS7{ayEvg&AQrv&DntAwl&9zBjDh1q0wsCEII-~3NiED z0N9B~!A4D+g$-x>?%y>SYq()EUWI+&1!ELC^Nk9ftEU+mI?Di+?;~HF&^dhNaO@nr*crj8 z8vcwp7vo%mpAl5>5mDFL`0UWR&d50!J2xtHhP8FrfG=3!3>`9mq`(=T={RnVS=@|( zWevB(bMZG~J8WQm$V@(CJG^vDKnUvFa63E~8)}2?kQM4J0jXpUh!fA9B_Ck-EJcqR$Wh2VmjU^p_Qkpe^2h+ok**uljrekEx>UWCD-Nh1ZOa}m=>=Hbg9USO&_6VLbtAB4H~C$HfW9j#}1TZ;J6pa2s!qT zV}$0_!bTV~BeaHTIu}WI!;SE)qKY*eY_tiQC7BCufh_$8vOu_0*Z^Z@fY#th=i<6= zumPe-GmJ810w}}uY=oFYdu+Ozo~7IRhF-z%c0DgemwO>BLp#8Ms99&K2}4e7*ePuADgNn(8k%7(a6IGXUn|4*sI6Pt6M`W zol8w78sk?ZLB?>As|_1_^>D*L^Z*O&)p7SgQKpxHg2bxkT{6fopNj;o}D# zbdB$O;a#+Om9Tq`nR~VdnmU*8S~OOil%4{uWK;rWhRS9%9_sA_Ft52WY}{hdxb4aI zVABwkdIH&KXgq@mo=%|6PlT=6wBEDZ{?OqFu(t+w2xVkARKkD&)|@X40BwCBDVc~4 zTU(2eEp&iJ8?Aw&YQ{oTU9(Zhs3(k_k0@`FB^>O}EpIc0_! zn44Px1kaLb#i+?!P1lWNHQ8+0hg-0$%DsLjKS?jjdeW#=nlki4HqaP3s$pdulg!t zUmY`FZ4GI4F3sS^S}a?At&!i2rNzR~VIyj=#a7SjqFF5S$FbPzvxR+Z%zUgVRMPOW z6)?KitJU3Dixm^-^X6pH*w$?}VrCb~XhG)kk7KkErwdzc?5x%rj0+f7f%F#yCPAMP zNT)@`048&gLj%ncP$ zv9nl?c2CLCSZsCf*~~l}3W8C}4n+3|Y*c0q zH#sdM6Q!R4b(N4n2ezeWI?RpeJqZbr`HA47QY!KXB9cbLifqC(&(176GVGw6!Ze65 zGWZb!6^URLkWDVD00L~g-&Gc`d53riO^w%7LJb`l)qs;#6965NRt|4#bSet+$sm$a z3og@<4Edniioe0x1arxubD*7{3KHqQsz&4uYVd)Wv{2 zHwluT&`$tra3Ilb!3fXQCH#BZ$GV_c0p=P2V_9z1haf)KrAQ%2R)D(ZLNx31;vc9> z5pzBgF{Dig>1s>f5cJrPCM)po>!8nNfF?ExVvI=Ol?JtXU5$w$)<8?0O(IoClVQk= z5NihJUyZx75po9SqDsfM7+YJkh=T%|S%cw0#hC<+WD+FLLBkM)gLMJ)C~2!K%H#E)ble z-Hq{xXq|{CPSH3Q(kGJ9*Gc~ed?I>yVy74@r$}3aBGG~*g9p?>>7Hg;L6;>?6*!e= zMO_kMsc69#V?-&1jHpr6ikQ|76z|TtM8rlIlHO~)LI+J-Ll$YYO7ezAswhDtN`eTf z8&(o%S!ZP3&_xSIh$B-RnO?f`95hIyTA>=|6hov$odeg$u^dM!GAm2GLb436>pU49 zU?>t~V8)Y>a)@j)t;w*6>lcua3CY0*Ycm%$RfEIQSk^CSYiJ*XR762$6cTW?tfA?m zBpJM67%Tv7c}l0E@ram?4b!m&+fI@8bH}p*f0&Lfy#Dr_2hC@iwg%yRe-uB z&&ryvXo8`GYB8yCDyxfDyl{ZZyQ~0F^Qb0+9adXTtmy?`#a%#)Q08_&MN54!(aJ4R9^y8)OB{42hS8vHpL8& zPxF?-YiN}*bb&LV5-PJQFUzWI2#QE)hN^P9$VKDTG0$QoeQ|888?z_-%X}jX{bjiw z649dh60Yc2gB$6PzA{88NU4Vwe16jSaw?6L#IPeDEy zSw?jKl1Kf4J#OR38NlTBXDON@eC=oD;09|s7Y+9_P^Cq>T1eEB# zrDe6c?1)JPop$hRpAVOP7AtxrH8NsFhYKE#PJL>z*PZXm?Ldqe>?|idndK!JULVnw z2<}Mmxt!(s#kuV}f&Kd6Zfjrc$NZ@g{He4>=#m762&*JfgGO~uVq}_ESw%5a_+M5< zronbi{obr`B;G(yWE_cO!;Um;Ygi~sWmrRyM4FU!lv9&50ATP8*tH>3nr1{}*f_t2 z^J_T27K}H{HN%B&m1C^ouVj(qBp|RbXh0%UDkU4bqJp%TLg}n5%6v4&ACAjmDUYJ@ zU|`N91j&;%dmhq3p%cVX+r0s~_&f(d-2*(#H1x!q0wj43Gel2|SBudoSv~AQ2@^AM zR;^gX*O5p|Q}qmPP&WZzF-Q-KC}V*(Aixm|Z>}xr7MN2o ze6MkK&PNc>kuZ(A@@~OXi=+6MrQH~77%z$zR6#H_PSiD4Vrfa%6rKmTrK~`Kn~QFB z9%4(!_TJy^BT_--ti)^`PjUBepbR#)cf4v3mD@Vfxg8+OF$(?ZKs0OGty{B*;GVGt zCldvYWmJ;Ycv53InUg3+Bf-_P3?Q~iE~+$peZ~8bMPPp4CNSsY2xG{8=u% zRyTbCU@qriT@Ift?44uhoh@R{1d>$@N`_u}L(`E9AT-L8bviMrtDD zx+xw2DUG#Q`us7Z!_d}gTdU`Fkpg0hjJ`$xqRWs}pDi2_$1Wna1{0Gw9hwj!T}1*R zIx85^lBmdvF4LUB=t6X8lKOmXwLlN7&SL9hv2f4@^RW?E5&PJf`B-cCF93-^j!A)n z5~;9?CX3*44IYe@5_Azd`y1qA*|U_sD6+J%N?-MK{FjFHGovjag{8i#CuaIslQ)3s z#Ab)3zJj`#5g4!V!5UpRLd#f&l;ROdbj`X^tx2bv5}ojt*cXJ(5fxXTE$m}s=VPrQ zyJ#S11j!Kw*px|D1}Gw9NT}6VrFEKCn5b$(VIPYvHj(JzE_W6dx)>gnL_Ax;4laN9j=10erQg0h1%}scMD+@&6$ZOp9J8s13(0b35lZD~a?ASJ2EAaYAHD)4Qvyr$7n=^@nXXV0I_Hpa__ zW_6M56?Xt!;vE32HLMdDDGe0S0G{V$K@Yi6T|n zph${UX^rJMMUiMu(`iTi-0bUP9`wK;|7J?P9V1%AWBcRr>Dq{WRtNmAdMR& zW=wJy6%#&pfeV>&Sk?si91{{KQ|shxRrzF_ohAj2PI1^W6vW{kZC4$1&k8_to3c z9qf|kk+gMQ4>=k5SD_`Rlld~hv#M5S&6&Q?J7?Q%n$BMw1A?i20;<3yF9TK9y{1ZZRc7`Uq&nRqaOWCY$gWXjw@FNw%Z) zp{ldYneX;{%i*vv0vsES5=X#s<3eLaI8D{u20vo5G*uBJF~|N7F->$7>&+yYR|_vf zW3~vj25b^YXrvPj0Ray|cGFN)mS;Hu@J--70W=ZSB2>Sy@7fv%-^KoM}tRZ66l%sOazWmjlcoAkK-DE#e4&c?GC;b{<8zN_?)M zcxOmyz8*}*MN!k@tYMXC$&huC*J<8RSdG<0M$<@2AyrM{Xs}Ccgbm^5$DZ)g7NF*> z!H-0cp5_=@k|;xB4cU+shEr9R7ATp8zY;b=Y=rgr8>|*xLwAa|hAI*Sj-m|>gv~`j z6;Xl+v@8tJJjh^@EKNm)0mN&<5LqbpgqOG{vOAoEs-h-F*KvkDA@+p0M1e{a zyfugp!|;m1>8ebrBFnKr7AQ+P1#nqIfF+_79l#D}M}yq;I2^si!%=HU8{iECT3s5V zPYK#fSRTqsAeYDpB>c~5q=3Uw9FF2}H0p5F8kz=JNi=~XAs52{!4Shhxt`}CEL9ZJ zK+8gGgxCnN5k_r<)(|s-21J@1(8>cF3DR&;c!LGiV3sp@N+(q-iJ3AJF{6fBx}g6< zMi`kOyAZtOf>LT~dX{eM3+*5tA9{mLhnIFeFGZJoB@%Rn`KdsMh*se&A?&Vlx66EI zmXS!131+8PHS~rdYX`3?vV&voaDZqrHxUOTG`AO;y7(IPVfP#}_bgb01{s_{ix6<@B3b}bh9amMtEeg>*=2?3II*#N zhBi(dcOwo39CydOiBqr!H$s^!QU&n4uEI)M;3QrVNLm7>Po%ua;srHcP>0vgC`Q5y z>XDG>4gJ?4wb zR2^J0Qa~1U9xA^w3q7L(fPtgZE5~s+j=OQbo2af##gsfnu!gG=R2k6yibTpHun@|? zq)+Lz$SWj}eJPw06{G{ZXY8H{cOl%_!S>PI4#DowK=&wt%hAP_!m_Teyf&A`TTN`E z<6gkbHftu-QsuNvLslJ`Hr{8hp zvdhqQofV5d>!}^Ej5j& zSTY4Z94&$*HQjFL4gwvLfMJ1#jR)+At-zO9J=-`UtH_tu~)vl~z#u!>QKp>sbS)E}SQIWx2OVI1C zqQ+W zsAEFtPwc(1d7}a~_TI5CP+P-~iLA)UIwgvVq_P4{qP7c}7dRGNI;{DkGVl77V#(ax z3X+i}6l|NzG&Nl}lGS8A4G|KPWLa6MxvLsbMg;(6=_wAm2iz)kaVV13^>spXx=7+W z0P2pXDEQVj*VVA$Y%F(>ATk(m!&_n>XajQacWdYxfp+L*;XpfPfwpK3Bqo!9>=ZQ} zVrm^ag;iN$708C^0?o1vEk?DDuFq821R<<`7HexoV_P@ah?!k1o0UjYyyp*LvJs~X zTW##D)*1o~dM#+45;cL8ppS|~LRipI#pw#iA|cACX#DH5S}Lp+XZle+H6t%pE*6Hi zZnM?%x@aaV{&7sU`fOo~jh)3>gLaXE4(wDSP;%%RNU;ktP!6PBZDccQL#{mVqGvQ=1G(ling3&bNSppqyZ2yGt)r z)gTEF7&)T%3H*6rTccZ_Y*0a>1U70H8#wGmT*yY8F6?7t=VPtmvp|lH)1WCCqy)iA zC0&LzGtZDBNrAeRreYskR_;dPbm90rCiY4;z+NHOkJ3$H49f*iJYgT}k9m?v0faZfzX~-BuTM{cPk|HBIEs^HIn0X+(YglA_w$(-kRI#?&U}PL{ zLJhXsh?!j^t0i&1xI^NlO-3nNgHw^Pl7;?%8nR6SEAgVNK#@l@SdIocJK1Qo)mDd9 zv9?*9eZtwNKdi8pXbl|&ZdD~YnF3KC4n%xF$bgesid3OF7=+fNBh0HeSA0GL<3`2B zp@99OIaplXxy1deHNX?8@w7mzBoICWKv|MSP*n%Ab@d66tbpWZS=rE4o?@Uw2xz4nn|X@J%EsPaalRPUecT=* z*;D8>32vc_(qT(h0k92w?Cv5D&~GPu{H~&Mw|`i0V`)W+)9?0r%$I?F+yLiiz2M@= z3yF~wu|iTv)^Ja>!OtDu!EMp9hj)g8$i?upSDkW_&( ztieIb2^x5GjfdPZDMOPjr8AB8u@N}(n3s1NhoiXIOIZo$i!brDtTpTtJn0Ql;W%A_ zrdUWf$+BTUfK1AQp$ekW!2TVaFK$RwjPb=dDz=7wf<{@2AsI+HLF0}NH$4@OV($x~c3OuyTLYpiAp`f`3*JN=I zPxS*A`_`EGR%^H?TIDDWMZlV0mp@Y^+3N*M8CRf zZi62j%&O5nuqvwDaBRE?gZ*zXIdv3&kB?wU#KsmB6hY?*H>=00Sshl9mI7UaW`Smn zrgTZCSspM=(2m29hQ?_MB$uM3K$SCOH95#B0kD^ISRq%_3Mo@xAl!=$##ZV=?cwG? z1ndo&jukTu*&z%AcF0{QJLJVbk{x10jG^crLs~;92?{ttFh*5SIaXp7D4807Qc_43 zI?;h=t>NDA9A+4;GiZDa1_>mY0hX%oH!i4q!9@WX$?b!c#~o;4?Ph&Two1ohO`C^(jk{Bak2_3lOP_;8z7OPsUigoNiw4g zQdDg&HIp+q9R0&&W^gzf%W%{h%134u8n_pLPEPQ&v8&r4C&G-?_k zS!QNO!+KO?0v#zg2QRrGdzqS^rQ7;~K842z_iUa`hnIFeFGZJoB@%Q{OR{eUC|GC} z&@W|omAhT$JF|>Lf=m!iF}iS?m8nB+PLUlPYlj0wo7jmsSU{P@6?jeIK$#r`YQV4s5kR*f*A6-tq8b(G77M?^VkcOb0%uFhg;xVXj_>eNBlb++9Vw24IYh{`*N~VCtxz!1A!hX* z${O5=2M!2HQ&|YTWklg3ahfDU@Xdg76$|t&4KJ|b1$6;7Y;4%EGi+-RCMXzzysIH{ zBn6E9Gy|Dx;4EcD*lMt}&P8L`VU`HC76SiJdpA7ht?9hdWF09mQ-zMANgjrdHVYQk z$f@p!H!0N6K|mvit(M3M4jhpYvk}2kl{BV2hQokByy=wFZu&8J=Ne zMx7u5|z`&$XJjswc$%%4QU@PonO$L|f!IHSbgEg(d$67;C z>5vl^SrVH0SkR0#7+BP*8bc|Rpwf^@Zdg=|vro}S#gO8QN7dvgYO+B_8eEh8f2-k- zwg!)4fQ=N|-ARq6px;QRL`H_RlS%_Eg#u#DF<|en4ydf+DC$la#fHX7c9`h+O97TB>P8HTS5aBNN}=uc~&fY{@XK^qB? zsYT$9Yp|;Y4v&EoF#}G})}T&;t{N)n;;6vq1YXpTB$4HKfGf(d6}DglMTGbv07Uew zQ*5B^aW=T72epQ5k|;x_LHvf60ewiRh6Fu7qyilM6gU7fx;}gCLC@v}#s-QF^miey zH6RlMU_*#!fdop^S&{;EbZFr*R7#Ky4LW$DT9{%3#Rlq1@VT7j`Ng^II{`k)2Z0wh z(BB!THQW*@QHr9XhP2ZTGT)RcD?9)Vq_?Q#9BPD^bQ=wg#xa)8f@)h%585df4+)kytlvcHmSmUimsY zaaLiBmu+13YrH(8e4%cv3j;~L{^b6&tIGvm}qDP3r9I? z{mQ_`Mz3FcBc={hVW08UV8 zC3zXZ{*=nIB8V_cqQ)tTs8dkPk))_ru-LqT&^(58RM@<+_dY9wYz+;ik~-v%DM_Yf znFZ=`nb#Q5qZ3({))X~5Ix1}5*t~17USRKCoZW*-f~+&B8_AR*LVJV+y@tHTi3VP` zmb*QWbqcSM8x7aQk+8q)k|&^JtO2+ff!3i>U!^EWE6D~(ns78vshlBj5DD`*%M@l( z4-#HrYX!+IoMmcc!#-mXkEKe2peqW=lLpB8k_u!%NKF$AS_2&-5@(sPwc?zxKj!F3 z#u~gzCPhJ`L}+v{fJ`YP}b#GmIdui4Z0I}PJymrQV~R6lzHg) z$2no_Td{9F55CnJmWibWAaj+0!xh9y3<`o_k~K7uf?j{1;fMvHYX}H~u}q1Cnwk-h zbHX?$%&^w5OcEr2fzC>o1c_udl9f4976Hvz_H*kG+;nSf0L zq^nih&`4^U$6HqFkV?n1}(9`|3(S|4P_mk;bI`HiR-YgLRY!VQzPw! zIVEy)JE8wOBy@xyoe6g#G!cZXKFo7E0x98@I4eB)#s6=KYI4>vPO3^v8Y62Uf~!H& zNt1P*lSvvitaGv<#9$4JMKw+DTgx(*qUk@95^nW{44W73(P?Cl4rdMP1gf`&0A1-6 zBk(Mgb!61IZm0&!>KdmhG4QcC^Mo@`Sc0e)8C=d9-bn+25>W%p6Jk#U?E)yvg6t+h zib%T z0}-&dp?qqpv&@(lm-uXx3nX#!wP@AR888`N?XWfIXtSe*?3&*bpyuL$n4wVku6Q8J*Te zl?FX&T9Rl=hNKW^>KT+oM%9#B(<4^=J}{-p`U2BBgf1aqf)E$N%Em;d6$S`vLR?B- z(703({y=dlTrC`x#w;qe1|<@B4QPE;h(kG=l{G~M`fZv~RbC`%MT<_MR!-}Yf)<7} zS>P%{OFzYy+#|`6rrYhXm7)_8Xpt*b3(zh#JtaNUmPqul>3SO4kZc51Xt`2T-8Mx} zv%_F&2`Cby{h=BvMV6X!w^?~cx-DC_fwC=~Ab_-Of?$fol;T0% zt5c68HNLNnveTUy2OJ<;DcclXBkZXf;qaCbS#H-5BGcoJSG^^$3RTM8B_(dB$4~Sq zcey+SM~R+N0%&ZIUTwrWG(|HvhQE8G*_Plr1sU^Nlx+3EuJRIhQL&%UOI-Odi?bDN zgVPJ@*;2Kho7hX+*1u8>+!Xkk5VL23MX-{f(UeBQUka#Qz$HtR$f}a2Go%K})loIw z;AaAUCPdlXgP#ex!T&n^u7-R*)bu!Oa3}#Zt|4Cx)Wj6Y>d-Q)X_~|^0J30pL!qc> zvRC+-fS(EYnNUBex(Os13Dy+)??OBicx!+w0}|Sx+5^xHL4uY)QQ;MyR%MZrcm;@R zIQ&c~3-q$$wL>hLfN`8~L7xfMU|oQeCMAP6M3G`x3RVpW9|FqS32+wk$)&mR|rdsSNpMA?eo?kT?{P(uZ96_4k`#5sFWlyGEISBhZaN8Fros8gpzcf zY84;}S%ztrdTItF0kOqG>JD4%C0;k{&*t2|gS zgBuCGxb+t;T*AP291+JXBDMw)(*;pr6pkk;Ae{r%TTx|62$5w@reqe3Ihwpyz5X?P zsblV68$&r4n$^YfuLAoATEK=*7Dlkdj3Sb^h9XlK#n4n*RY_H5X-$_6V6y^KWi(Bd z6fUYPG`7|T%iUsYy~LwoYj`tYydi;Dj%O7`VP%<K$%ux~vtLL}H) zFL7(Nh6Ga>;LtEQ&?A8sUkcW)D#!4ozzYxyL)&lTbHm{<3P-{bOs?2kFL7(NhS37) zc#)?C9Z1p@mV~wnT?A0KNU0jbsT?Uq;aj2ZS?WRp&T^KGpocPmu_jAVJ+-caC{UfG zc?l&wHMax2OLO`1%iTfLgPi3clIFydgCera&@;BMIJ8FIYNoh`=%OlFVcH5&&wH;_eS0g`dA?!a!dS&5bVu zhVsB<;Ei?QozKlxj#DZZZ z`(Ett_QTzfr|TKE^i;^r4OGxeBSp3+B@zrBd}-5rXX;1cR+OOPF@dJh?@uo+6coC%}UWooeNclKp7Cqoz|PG4~xnkEsLGT0aV zZgZB#5!pn%IlX>ygrm2S5eL>-Ksa2bWpSZq1p;+TL>M4?8$-jkdM&PpyWCk==z?ef z-dT%*A(kQRl zr;QL8=tznZtxF0E4fb3#PA;!z z(;()vzR(NOvfCV9`aR5P(cvc5$H#VwF4mL#0XHo3;m-$8( zVnsVfum%Ux0H((atj_bG;XyK>$xbs6%?L86*$Es+N3ra~9x;;E6!TeJ;(a`~TYJ{d_0#{~I%cx0mTAH2$ycR6vE~+^W>&Wfc zGZ>Ph<4HEt(F5KeZDuF%F~aUDce{Lqd2m9vYlP znc~_` z=mcW0PRzhMf<>?n6c1Gy7-kfZUS>5xW>wh$)hdHz6$#k;ql#5CZ8o#ni!%vh(ru{q zOLiourzH|e-g5VFuLtT4aAW3WB&Q(;8Xo=bFu6%H?TQMoWSgUnDKQ$Zcc2}p*g?QA zEF=TrP0?Cw57mJ_rG`FAm!UqhYL;hAXoze=?1{`1X5Qt5p=OA zPSymK(n(5ZNlpZPtZ4jpeIQ#dfjVzcAT+l1wzG(tT`XX{ME!Aq@e!vBd-B+Ma%+Gf zU~AGiR#lM-7RwpBWJnSRJS~DDG0=>Lt+vM6IsA-hbQo?!K{&SBOI<5gw1#Dpc_`%Hpb6}XLYf>EC$_P+Mrv}8Xk$E7=u*> zhUK7HR8kd4IIF55lY&8l1SJ_23?d`bmXVH_iy;9Lp9#q+$hcJJmYD+N;%qm6&!@{dkJ(LL|t4^N?hfHIUI}@k*u<(C#xo{c)gBMD|Z9y zurJT)Dadn_J3YR9m${){9IiQu@HYUJpg|{a|k};k(w`a*I$${(`{FVua56%)p1Y-pe6s;kX zXjrC#!(?ej&{&06C8!QC&}l|-q$cQGG+isPTNoOt z5GcbloRA_e@eu`GsZv1cYFz_^M;M)vtYxZlatc9pq6s_PhV>dBR8;Z|Z+WTH@Aeec zYjNhd$+9^vvX|No3PaulTzeOSeN(gs`!Pt06BN=ANgm`EIT`mdnvX_L z3g6{L9x&~C5|#K_9raTaR9DH76n}-B_^815WdUtU5=jS z7vx!P4X^^$ZD?Mb@sFb}4u>9EK=S419lgB)+__ux3Z zIJbQ#z#aRblnCti7ojvNT7ye4kn5*GEK5~I(87SyD2qhWAx5DTfH!e^6s6JdU2S(j z1|&0sV2KpDua0DtE!@y&4-$DBS0BMS%!L%TVzjP2`>6PqzWBGawQ9ctf=`qvLO%&l z!rd$=6cFG6kpQeWY0zmT8*)pd$ExP^J0Ueu?j8!6#sY*HwQyV4G7wQ#;>>r&5&Tfz z%*{mH=UQr>nG_^sdur@%^4>P!uGVxI5Z~Mv|K_$<6c-r5fIC{_G=XPXMN?QqVMxWG zd1%}KMU#fy+}Y6EL8N$#yw+~%$d5JB5rpW!Bag%ctR;D=Q4n3LhMvM z$>gCRO&SWzPwlungEgMu^f0z5X4odlTHRHU4b32VT87AkWhg*5wAJ9I;7Y zllY%932PNqMUq64(Lj_>2Y9JMD>7>c8c!P>%kr|!He~(l@On#7@Lvo|32V!To+&C} zPjvtwt;R?Z%Y)E69O>yP$!VyKI!3EvG;sBxmX`(?A`mNxfMl(Bs&TZgYb1}*UMx=m z!8oh|K(t;Y4Tb>#7`~x_<1{S5dhWm;-_S9;p{>qlW=%WE(8&OR5?*59h3YIza=D9&;Wf|0YxGXEH=d@@ zOQ2#d@&PL|`W41e6z@uMJJ6xrV?` zo43^If&IzAV&cx1Jw=eFC*if-?<&nAM4?&JZrz$Sb$J3LNi;)6hE3Jq0ezyPzu){$ zj`9i@+5_7Zr_Y6^R@YGtce&567CXxW-y+2sd<7^S__XfH_ZFatzf-o`BYS-A&<5C7 z^TZJhBcYRPgFV^q0)MfO$e}rsXx5Y@SQ70C{Gp&r4xF0#mm^7nGi4Uh7W8f!`qk0a zW=+j~Xn#F_(Yr(AqJNt;6=)hRlwkfff2Syd<9Gu7j{cfQ7A5m{9`=dB-_btwzUmRs zEA)=gCxP?S{GBwnhK`D6hmOm_Ui5q9zvx(@*`t|;z8m^i2!7Ad{=oMQ%`Ws#wAY+j z@EoAg;ZBOY9pJ1-mPHMBi65GJ(0%E5X)d^UaJ!|rJVpLu0(BIUp;r_ErG5c!VKkV} zU+!|2HfuWc<$Wuf40Y&RyZ!dXi|ZFpO}+f;t3SPabBU5f+`&F{?_ZLBT+!s3Rx__o zI+WQvP zoqqGoqP_=?q)e6UQ)p)S|JUZu&Z+IvR#j|lcKdDH+CFfLGUdv3tDb6^_(H$;e(n10 zh{qChDgVCz9D2I4#h_-rdrY3=x$myqvuh$W(Yj1vZ?I$)RXVHWw{<62|>&?mUjs9D18|Ag%^om)Z z?`eDdEt#1Ga~;FSo*E`R&}QnYqC=;CxnZQxz0LCPJw3;+%f5Ba zxAVWS{cz9L2?r;vA5dEP&8$5wk7idegB;)e_|cNT>_}zD&uSZ=TD3O&!{>kOc&Osg zu_F_!-Z*;S{^gZRJ~*-Xpt|FM9oMg1zw(LiouA$??qK=@aVL*`Z||^i#urI1PCop% z&t7wM$V@#p!X63olZ6z-SffAZSUVoUQRu<^xpmV_9Hrf z-Ts-|Z@J-x)=QbP^#81U|B8p&UB2e3&#sts#hZWLe%-q_Z))`m`*Y^-2ZlR`|8!K| zqpt5U0Y?HM*@)$${gPEP)D z_NkNhk=LKe9Q^sViC6WUOZRyv`^D*BWFK!o+hH5O@IV!P`pVPOHKqHQ0<&cphMom5Sz~RZsv%YWs>G*NG+jdgB_x|kt z37zdfWF8-X^Jzy%uERI!58EE?dwRmJH=q7y#e%6nT>0FUS=qVBpLq7W4hJ8am+kZX42SNb4cDExCP3tBqslt)95^ z*QNfKS2CTx@3QuK$=fDz;oh&OJ^bW#-G-I%Yd^U0&L1-NeQyGe`IpLBg@7;)_0e?RoX6(wg*oO=4;nZh$~oz8nA_tcKfXR3bN zc>KNp+qmKM=O^CWmbc)&W4}#1?LK8cF?(Nn;<7$7q`znKO}*?r72iMG{HKAVpX3JL z@Z2}~Tc&Smv*o+LEuUt4Ja_7?XYMY4ps35k-R|h-cwKq!k&lPnvSQth&eRE=C!UgT z&$_>jE&su+o42Q&O73#(vZ~8Rct^Z*nmHwCV@EdsVdvf3*ZauGH&2gB&ODR*WbXCZ%WnQnf2s59htvObo@OVto?Y~#n=Za7anMy=@16Z) zkC)Wz*LHns?PXODJ@@s1suv%aw*Kf{U3;CGHRMOBg`&D!H=91hEtXE-zwpVyUyoQg ze&wv@N4xEx)%9qX`+DtIKeFnvZm(RwC$Z|evFlf`PZ;ZpTlC8Rad-p@&`)9N(Zn4c+zh`jO*tD;El{6pmSn^ZMiyi#Wef8F( zoBJRCzY!C@vR`R`ah0FmoWF0-;(x@uKHqf17pwMt@xjacl8^s&@~e59*Oy2+A7m}a z+;{8Zm*zXyEZDv2NJ)Ih{Ga+rIqC1MU0rh1q%Zk@joEkQ@jJJEl>5b(_xGRl;-j~I z@%gkL$x$Cn>wE2p7gzjyz`tJF*X{T{BgU-wYVp5>O_SoD`*>}?d-50c=`!NU6<_|P zPw8`?jN6oT{JPU=qrZOM|BsV>=w8mbB`r@0Tff@w>bz-0NxQ8d{~9;uN7b&%F=tz4O=| z$DD0b?|Z$Q((#)fm8-v5UDVS1e^kw=nozZHS?+zWzA>T4r_(Dl_cD72RE(*5 zz3Szvcb8?w+c(bLwRd>M!(EKl^E{5cj68=hx6|B7mCsjZf0JFg@tcj6@q1hDowPS+ z@7BFp$68fPs`6GWuX4E!kry*pS@AcwSwU0-?Njn6=cKZT9v)_CnRfszp^} zs^(Ws9-iCv)hUkmibibicKqs76Gp8%-gz3U9vRTP-@==wSAIQUz+3%Z&g#|c>j48F z?>Fv;_2U-(HNLX>{>KJ=Q#SOrCaoU5?a@|w;!6K*gKn$1%{Ow_FI$FfIr-#~r;cRr z_I|Zv@@R2Q;u>j9=Z-JWdNMcV$Ut|$dwXr97bfkT(O zJg{ri1ILyw)^c8pUq9vRDc?+4H)U4(9%f5=6Zh?--iY6rRorn=<`!nlfYLF?UqAlx z@pl(zbzHx}y=%+x(uWtXZN7WXuE&PlB@XEPmsZKwO>MLJ+3%7nere?%`(T@G&;EB# zn@-Q%{&mjh!cV0~`>)Wv^OlTnw`c#c>xTDOF@AgBgVTmoF5k25SnJ{GE1ut;b8z~J z%2RtzAG4Lc-|^VIqw|gpJo-=-aZ>IvYx)~)K7V%qoZWK{elqC$#Fc|a{`AXNgT5W~ z@3rqgySsSKnzaWv?%sRk=*gTK6Biecx^0Z~s0$ z^S7ldKN|JXC`aDjak<~NwU2xD{RjRwd(+eV{&ec`(_f^$&FsrQ-tyGs(;qC@)OYaM z$wfEDwd{98a>}^75xt+8GGmKv+Jw7KzA|cClNEPPJFnOO^B}?zqglUd*hpXKO5ho=a?C*Y&XoE`*=vPM1 z89ih4>jx`O?3w!G)V)*poH}+z)$S_!^fM3iIisAhb)R;7<GvDzt&nQ2qkj;@u z&5ZsNTJ%h9nyOA zzh=|A(pwiNE>6k0FXy@VoMijui-#|+T6|m18}lEEU$f!s4WDe-wBe%--wgie{1@lH zGwzLoP5m|}^6Ks!Vc`tSRa{Ldyu9?wf#y{6Ryzb7`?mc(xak}3+`q2fb+nA4>TXUW5x|*ZydX8?8LE| zV~cK^*J|KxYwl^b&AW7m|GixgY+aF==>BBvj+qCy9Zc%}^VXBsb{oIr%Y(aozvTY* zr_s-J`r+vh|K8`TssA1Li*ICn-fH3S^S{jdwb{yv-rb$I9b7i#o$h4^D@NZv`MZNh zo<2GISKn_}c3<`0u68G{|Fy@S9(&sDDcZB}=#zeV)Q>wcj;-*Hx~jYcf1p9{a40L+-tOd9Qbx+_YxY zXQw~w;(BO7M&Cd0-`9K>HDzX8@8>$Ccbqq&Xd>-;`0(Es^c~iB*nPvUS-kc-*OJ5C z-kd#h&OZC`j*m`g^X@~37nSFplu!0OnJLYC;ht=+^3~yvH(ShKb$I81c&_)cxt|Vr z;-%MS&D}Gg)uK0En!h32H!Pn^FP(A!t9*9O+A3`k#`t>KSU*c;rCWR`hH& z<2w7B_8y7%`{~)=EtKv{d-Lr_-hPf*`{H-kipTq~_w?P__ou!Ghpj!bV0XouoGGPK_D^Z`$cJSuJZ(Ok$OtH@;S$Q zx0;sNV&m|^!v_x=JmUD!?I%Y5=j7K%p52|l=KeL^*0h_F-gWA*<0Gyc+U>+`M{hfP z+wqbAJ-N>Jk?$Md7e^-U9@u94p?5lNxN&v2q9&W~82rMh4^}RGaqt3WQ`>J7w%)jW zz{Y*cwiPbf+G_CZZLT3(yAJ;Fz@i?Tr|+i@bpN*KzS3>GH#HyZ=+@@UJ!kGdlW^va zsTEU)WG_vFUn@CM)D2Ei1;vwVAZxmSay<8P^?b+IeNWxNlmI z9ksCX6PIt(>9M886>ZiO7qio{_i)pxf%|@YVw8)eUTTqj%a>36aCE`ie}5wMYcc<+ z%cpE}zg zE?vEJ;|s1Y7H4ltZL+r0zJX1ANw3d+xO0K~$&oEreD~mYKYV@LBOm_s?VfLUeY^kL zoxiO*J^!Z`8}Ch@@@(Osul;oC-LE}2XezaO>5dl$ev$7Rb8^+?J1@(B_0TJCeKPcu zp_}@Ybm`w^K$pTUU;S`vucv0NE6IQH&_52%I5gqV!neNd<9waXx^>Mh^Y(PhA9LvS zL$}3qQ-lpIT&ohNf7{1f^0ytgv>bEuQ#X%EUDaz<;ZE1i8+T^zh=mjh2sv#&^abL&rsTHODp@L9|KV-9Wj zdBf3F!*{;B=-orx|LiUGPCGIxyKDBS?6Dm;R&J~OuyTFn=apM4-=Da9@6NsZ_I|f_ z@7^EcPrOm|#qtl9uRZnm6Z*ls5BA#r<@V=Syg4-Q*vcP9ZCn2FsYgzv9?aRkWJS)3 zgF}}LJF|b=fl(Wme|T!jkl90?AF^b~(&9GLF)p$1ir&Ll z9~zhc_Dm?Gpzl9Zc>#^S*T+_tQsL9)0NOtNx6{ zyIMWB@|l$r#wPWcnZGW3-Nt8bh`;0J;T={??Dp;I@xz*?|I>fVYuDNzQTzMt%Q|)? zI$d?o!xKJu=8nk|zUp~%+36({=YQ7uKffOA(0Ss0&)j*##N^KkJ{dpgv!Slb*Zikq z=-qE#^G^AbbmluZ(!1_Yy6LUw`Ih4fTdetR^D~`}4C{LT)ZIVlZhvt1p-0|r{{6@9 zT{rK#wqW(uyJj5jdeuval7a4p8F>I^Y=r`yEY@nU)M3OeV+1FfwHCG-Yo?kN-J|0T{a{A zPvd5{{yy~@F0XWM!96cdIXUIZDNVXu)Ajn{Hw|ljxXZr2`-XjVWX^7C&CV%pr_7w< zn$oUI`r%dYUVY}psXy!=I%4}nL%yG!JF4qjQ^al)ckF$j^7OL&*(088_vy|fca5-h zncMokc3zKU-8@~MPjqL92mdN*iXpEV}u9(*M$W15Xb8h_WO^ZDC+;xlZ zJ9S;F&J_pNEy_93y6cEz2i7miJG1Vx?E5Q@aaS~HHtP=c(Pp;~+F#M8$C%d4@v1>P z+kHz8TK9bi;kTb=P4d1t{HN0OvvxeN`l-%E+bd3VJ@M5`qjtP}YWG)@-uur(|GYk7 z_>cen`020npYGIm*}liyO&vb`j~4HMto@vlSwyjfVW@Z_%S z`$z3R7VrH!$Lk$cwDr9u?=9cCV)F9A2VXtW{g_I+dw?%$?*JHOzDmbGs__1@bzbRM;AQL}l?kM-L%u4aU z8OtA=;N7)z#xn)`XZY{FJ!4X*8GpX>$Tk0I|1(HC9(kF5>DL>lfAL|L7Ot*0U*7w{ zo7UQTPPe~)*@pEu?M>=9=doq$l>t4LJKHYbbNAwh3l7_<`P2S%~r^bHglePzdPP|e>`!BtJbfpdsbIhRaehZ{g$D8Nbwgp5$Y$ZOqIKr-#sby%Z{-9 z4AO~28Ef`etu>h!=wyqgZmx5?AWH(wcVD;#A=WvR$En@G*nGPMLXflLj>`P;!1-bB8wZ*{Sa ztSw*UwKw7$Do7~kcHJo>Sv9%xYW?~PV#KT?@Ickyam4v#PW}0#Y@o77A5q`f|6tY)hqi#4K zMa}!z;KuDNUEZ!V+>}<17KQc8>??;$9`z=h7nkaGR~{RBJ@I=preo++L%#j&*G~fr zCK-L#^-ijuVB8L2yRvD8C-bj1g^rqkd{wSg8su?mtPr)}l^lDgVOLyDuf8MwtBC*h zR53wnwTB{~;x~PRx{AV-MLnTJ9@AI%#{3B*`Q(yVe4E;KN&O@p-*Q^I#A(LHsOG2c zce-5+9O@V%TCK^k=5nw?>0HkVMGrq0&4 z#y@T7zd3EY`IRj9%3yTM&4Tbnd_8fM!7n4j<9@j{tfQ|I*S&Y8kDY21Yov_W{NK4^ zgzBY}d@Qe-@GXk<%qp?eEanO>%%(s!9?}N>LeeeKJ1to&We(X;k3{Vi1|)RSRqZSk zCyT%PId|@6fDYlH==qYU#@tse%S~4N-U-!prG-l!sXxM^;v!TS2U2m=>9g}(^N^IO zkH^crGo3sf#N$-{bg$`tH6BlGz1&uqJ^8zv$>)A%g-lL*Nq@-fO7acQn&rK&4Lq** ztThv@5pq;cnm!KOC@1LyY|W_o^i22k_(*=d#6LxJf@?Tlp`(=El-3I>cT6J&dQJVl zhuA;S!?bIq{1y_|X7{4{p(=#p^!r3g#9`g@Q*280VxG*tTaAimV2R`koD1 z`iO_5>{4Z{sC?QX==0;w-5?$qXQXPI?T%RJJg10A< zETD{Rn(`in*w3*^=Cv^kg&zqnlRlsGeey(QMtN%a{8>}Ad~Sz3GulU@d(X`t`F{D$ ze7FC@*!vlz*1|`U&Ud!VHt(1)$_7JrMt8Fu`WW2MuiiH_pTv-w7&fe}wT|SHdEJTk z+TcI*I}Q{GN}gz06cmb#noMuc^3b0{i$B|nH?qq96qK|<;mZ8dp5*d`&79q5+yx%r zw)y6bUy)tHqc_j*>?`c4e!TQkwdPXHwwlY_-BP(h!b+!*y=y4#gikW9=jVKHCy*(2 zy5i^TJI-A)A7pRpFFVR%BJ|Obx_*PoWdp50P6=5|Il4Xynfbp zmnj`*{^pd-{5R9^$;k-z7gKn%bj`2X-b*lbIf)AoQ929hB@_P;#S~%6u_n{y%=k@u zr8Cdkg)4Vs+1dO4sN6JX)$d!)?|(#x57}hwk(bmBY$2^)6xpQhJ@TY7RIL84HD0Un z-Pu;!ohwt_m|fBF98+a-HHl|&YCG=karilD?h zy4fhr%?|Qgo$1Jg8u{9i8U6Pra@sb@zY?NCA6=If`yT%;>et+L7h>TRu3`g?FW&Do zVhR~t+)G8JTjjh3v#+#C&1$u(*F>FgMxEfvQ$DZ#>$HhxFY-|C__%gkn=(~rR{ueV z>G^y0mePOT6Sc|C?uc|a&`IrB6Sn2iZYEN9xadho+K^Od@f3&u=qRfA-A$A#{;A-$ ziKj7+@0_jmO>C2T(qQU`Jly%u`&y2+`d%JgXxp!(*za)YXl|H0WbBAu{$@p;`|H4D zhcC!-lYBGF;uA$`Q0X>z@$0Qm+(Mm-n{@VX(A<>|xVWD>bc~OdIFeP#SKjlF7{WTp z-RQ*bh#{4#yiR=`B=JP5TvF>!eh__N4YPqj#nJ@b!}7?R=B>J+mSV@b4W8txMW$ zcM2Sf#ABYPTL0OXl{v%Xn_L^Z>%RBUwXnIgdU&c`CU9ss65;KOSQH8|75eDzz46|f zH5I$kfhfg0p#IBM$#)NY*p;xSvFtEkihAq^Fr>Z02l)=VVi%Eh@73>^-!NS;X~in( zey2Ca=1-)j+3fH7`dlhjW=?Hq{xsX2ww9*C%CIf$kL4$o7;+dD6tOEW{X6n^KEeL| zF!4vL=Q`y2NMoWTF0QNmj?MFlm3E~~)k&EYjEEsmC7#4$`1trfk8D5Bg|WxqV(ZlD z%zBGSkz}s+(Qhjqc~sW)&0H7u2lPDKIx@@NE;z~k9EnJ&j5n86BFTD5cSj>iMJ_4( zmLQeQySLiTd$K)fwPCA+=A@j1M?X?8`=jgMK& zs3}z8;nKe4V^18udW1RW!-jpma8DP$UHgap6jCFX<|%AK^$Y6zhhDR~?(Xk4u*iMr zlyel1bHuzHQ71@ZeeMFb0JC07!k?k;dc@6IC2>vM$f)AHk|*(qj1RqH=fK?~xt<;?~!tz zH;v7Qch_L`bF^)gD=80h^$ut^mZ!~pLqONWPfE?m^cXFV$x`ogFxx-ME}Bs6VUfaW|wXelQW!S~G9Tv(jF8X?QVqVqdIr%Ozr==|caX zs~-ujeoVN^YV5#Glz72RjB%ocTKDI(KEdja-!9dET(&koC+!=^@s>V%^U&5oEH>9F z$fGb-$Rk3`-23H`Y$IoQ{EXGUkb*gdWx@Ik$c|B)6XxeL}IDnxSc!;U83? zzn4%*dUyBF8=t6nD>A9Ps06FAM|#X}>~s1;TZ^JtHv`ufy``w*tfDYq>NAm(FleP1 z_iImD^s6Ub?jp?$xUAPDqG{$GCyHBah;UKxEN#}}zBbS>7hJHcYQCOoYUwi(HcaOxEGGPq=&nw%D+OmSy+@s}uZxxI5_pZ<6ua%J4 zn}+dzm$!KQ(AedKq{b4z#*}j5li$;Pslh9qaRzY((>TV4JujB!HH3EQcILWWi+Wxl zg+3$nUuBSV?%RqLQ|rm(r6lCC+m@7E6uXUfEnDtcs`!{wk*j!pj{nv7`p3Gac-L>D za-Ul3idwWe+?u%haXk3B;+@1#UafCEIkGkK`*OKIP`b^JROH&+%~DGGRkRq9t8C%5 z!*!5vws@?*+nV7m#6Lg6tf6_X{SIfOZTAVgDYw=1>vK|BKVEC-wO)^RpuL?Oayc=1 z?lHPx=c4MnUHvAt_NzD#aMbdu+6SqPC-^xxzgMi%Bx-H(@fNuKdaGQ$pNKZ9zFy#F zqnk1%q*Q(*zGNZZN9!apgP^NCW+E|TEhui=KYWB+iF5Ai*I(_8nei>0T69t7aeQI7 zN>kM31a4a_k7MClGfd% zxXUpnrss$@GKaVS)C{}Ii8qw3l246^?D!1w8&>9FQe$SV~j4o z@9|)7{A(Tiab08Pg7zJ|?l~2=5q(|lS2l^9_DP(%Pv6_gWhu(KpYP<7HBA2NFBhS< zj+@*3uOzqmQU7G+JjEdcs*DBpP{uY>Rq5L{3rXL0@AlEzJU{QQ|JCv(OD%(LriHTq zf&dN6+j**9uJdX-8LX{O@2PNJTP~%(O7QB}2i3U1d-=5}3pIn5=80B+N)F}MNHx(b zDnGuubWI9#-9Jz5bURzqZJQYo9WV~Y&7_k1yp8ONMG3%9ff-}%2;^EzJk zu`xI!-s3EALmb`O^&;InAP{pzsm{{ z__H_Ob>Rq}NL^J<;x;m8)k_p=2FEWW-$?WePY*a395ueJ+4%6zh^N#Od7#Ckxm43z zOQuPr2!}qj)BkYcc8L=y72Xz(6L5>HkQd8MQI9uAY4d1S<^=bw&AYm#RNN_P*O;;C zVrlYda>YY4v^+DAf0JG*vu5*T)sYgjQ$Ci1;{sih-lEGuq_4rX?O&Uwsk1d*xh=X5 z`!tzdsdm89$NZIy7xdQiwVE19O-%~xwaG`qyRq`TOv9!x)r z2vZphf-@;?OsVVbC`4l$%uM?(C*a^wx>$8J2EP$(Km)#fhoiCE#s`)4qiKa_T-cn7j0AN`id;?JWWb^ zyYRs4?Vx=Bgz3x6K`$rQ#^+o=H@L)l+6Lo4{_`D|;*zw{g$K%32D#`Dx}?{J2|Us{ zvrH1oOzLKCJjMO&5&URtV?yWk3)YhLb-Jff`tHFh$hRJC ze!Jxy+{66I+9d6+51W-`Odo%|#g%NvBXB>m9KQl8!cb4C3)!no+?v&WtyW><=Em#X z%Gh?76zAK)um2>+6p5A|W|wpS`QEFoQhsT{yym$I&K}O5TDzZ!=d+`jcncJTnD5r3 zoT34eOal52s-&85e_ipLMH&WDca5YRgVm>>X6qK2JdOM2sg#7=7HYkvvXf;?i}_H| zC-`lIki$I3Kyk+IpxZEaNWNI=aGCB>aq;e~}7d{x)G8RG6ypLg(!bI=q?C^&o+y`A=MOm4u23m3|uvf(B97smpgYo6hxDla7vd z)^WIXR(RxZtZhox^mVlfF4`<_4vJo?){gt|t?`?*{$&|!6n|%D1gn#&3N`M0Pejzu z=J)ln3&NhR(wbEV!pDA4{eks&qw#Mh!R!5&|A2!oVd1W}@<-ONG$R=icwe^K5Fs>H zMVy{jz0Tno@e4MPzGRQZL_lvAJ}(@Lw&6fOt5fWIj-H!aXlm&1JS8td53Ge+OJc=C3G zpIXduKYzM!8kyY7FrZHOdW`aC#zqc0g`tFV|B$IKcZA9zMt6Ru_7f%*-^)raQp3)7 zzTFWHxqf61ng4LVGd<~?qLL8G1Mh=5;I=0R86OsZN_w_#nnX}Qjq&Y$PImR+lj{Wp zJ=hPb1NZS$`F;jWOS_64R!hz(wuiA*vE3(Z#`VH%`9hv1(Lwuya8q(nVBAmb=0zV; z!%Rb&;CKgfPT#9kv-oAk;?izY;c-~*-oNF2NjzD5AK##Q@KTSp%TX{Y+#^G=q-#sA zCLGDQmN)K>p*F~H8)FNj6F*8`BuDm@)QvTCLuHc07crcrS&SXsj#6toeru~o?!lFr z+%ykUf^A<0$-s(lXMi+mZ-|nas zzp|9E=JRroa@Y?Qa{FAdZ``Au8<@!0P&FQ&&O=;>#Wef0Z>tNEuW{6-&0J|Dlpcq* zsn-RPkKVQo2>Uk8+{F|39``-tD9w=)t+jXd3-1?1Hn;lQS|y&uOqyLFbAJ<{ycyps zFIC($;Ev@G>~~_sv2nT8nk~TD2y0aGSf?kIZC~=qjRpMzT$@_C;iq3@zdzTLvlE-Ds0q|r_%BeG^LQsZ47WW4619G1bIpcUwI*eSa8=CUVoIZ;({{*=-Bgv&>#jXMt!b+gPdy92Qc3aj{iFe`S{pUqNYef-W~{n`d(>jUXYpd;nuTq^x^xw{IJcsl#MK+w4>K7^RLjUEi10nH zU*OTEHds*8~w-XyTl}CMar0BLjkXx@boP*3XTQM zV1LY%q32iD*r!8%Y8r!Yd*H;cbLW~&%B415J>PpbB0Ki7GP$fLE6IC&$|Y@!?<#df zgY{7bgc}z8+w;tmSORe?*#x5^X}P7NsC#Q`p;whQgX7%=CJ9@+3OR2laB{_6xi>{i zIA`bQG)JY@ z6YrF6Fq-0Xze#`N*s=Njo#wXf=qL;cEX{k4bW*kKIXt$D ztzdhlmnQlsj9zwYmoQF{bYq^ZJQb~^D6#vx>aF9WGLktCX_e^Qt#$Erhb%ACr{O|6 z>o<#MD884COi~J{d7t;S=IEEWF(4F-ZV8tvPO8jM$>y^r%0@GOi_s<8v3`TM{?nSp zpYBgtg8D8-^GaUyQy!N38*lTsRsCy-mNg&oVZM{xc_qvAOQwD|@m6AJ`C4-srG;p? zM_^_2494t6PfV>M+aC2So-rX~p!#G>nl^T0Pl%Xedgho-@!&au&vNg50u?N?A9K{L z)DTaR)Y;lyOtjrl>R96KG|I`-8oD~@5x%XvB>DNSc*b*!^|Vw{j6jyW*-PzjP+tV* z1T&VT(FI!5mafi2w${eId}vFJzX$x8lF-W#- z)O69n8^=3}0il76zso<6Zy8*xx)yiNWh3>F{*oQW!@*&0Rk3QH8z?DXrOAY8OUz>) zbNn>r^otIJjjP0uI4&8ZYGW|O%bs@$zpOR-sN|1#ICXcf>8tUd7s$(2`t;&ZU2#PA>`+p(K7xH_-b$P$a z%froo>p}HvV?7n@99B`XYcus&w(Kl4EMlr7`A%7PvF7hKI8+bt*SZo^b*^EMDq2XA8asaNY28NPwWC4VO)me- z7nzw-SIae-nTzuSt0E&LyQN&DydNJ@=T%td{G6LNF)?z^M)vmITsDs1JK?yuR7GTu zJ-RpcF_L2X+C7YfXM#OAe*vG8R~)timG_eM9Y0_cM+Ui-hCUF5iQhj3cLojG7ljI4ce z82294s;KG{(cjL~^%9tyJi9|UZP)R3_V@xS1C_tXNO?I@vrIU=H%sl1=QRmEj_Rr{ zlsLXhGDK_Q^_*lawS)R3?6{>5Kk8)4dLW_vT*>~W9>0>?-p7642sFMk>uX6SZWAS| z&vr^DQtcOYFmq*p8XD9`Ue{E9;q{34LPb%BaePRhU;3xO!Vd$%>?WVp;>!xVi_akf z9+5LXMb_5$QPjSe#8f%LS=lMZIP8@E7)W^Jf*xjSuIQJe>quVZ2s%EWU}G}sMG;l~ zYDPB}>3&sktRDX2{{I=`*mB)8T(W2BQB9!kP+v&3yDReFGW z{Toq*e>q8S|AQ$nqPx`bLVahP#TXJo3?zYw4nnk@0T6Fsev>pdoFPjdS-dAFk*h?U z_{pza$&xjCr*_oX#BaUseNh|f>z*Bua*xWKW`^QnacAGF!$F2?A?M?>dvA)MRTYok zdlFat;xBpWmeMW4t^N28ab?cpQAyeU{MQsq+u1v_D%S(8nb$MJj$VA&3fZuUyrk$v zu-vT`Kjoh}bStNwOs$Xdw^�7V>r@6K4xO81y@vd5LJkHfK40te};Lo&Vw)jYo| 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 = adc_ch; + this->mul = DEF_AIN_MUL; + this->div = DEF_AIN_DIV; + this->offset = DEF_AIN_OFFSET; + this->last_read = 0; +} + +uint16_t board::AnalogIn::read(void) +{ + //Read ADC + uint16_t raw = mcu::adc_read(this->adc_ch); + + //Convert to mV + this->last_read = util::convert_muldivoff(raw, this->mul, this->div, this->offset); + + return this->last_read; +} + +/**** Private function definitions ****/ + diff --git a/firmware/src/board/ain.h b/firmware/src/board/ain.h new file mode 100644 index 0000000..55809da --- /dev/null +++ b/firmware/src/board/ain.h @@ -0,0 +1,37 @@ +#ifndef ANALOG_IN_H_ +#define ANALOG_IN_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +static const uint8_t DEF_AIN_MUL = 215; +static const uint8_t DEF_AIN_DIV = 44; +static const int16_t DEF_AIN_OFFSET = 0; + +class AnalogIn +{ + protected: + uint8_t adc_ch; + + public: + AnalogIn(uint8_t adc_ch); + + uint8_t mul; + uint8_t div; + int16_t offset; + uint16_t last_read; + + uint16_t read(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* ANALOG_IN_H_ */ \ No newline at end of file diff --git a/firmware/src/board/dio.cpp b/firmware/src/board/dio.cpp new file mode 100644 index 0000000..c70f4d0 --- /dev/null +++ b/firmware/src/board/dio.cpp @@ -0,0 +1,72 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "dio.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_read) +{ + this->gpio_ch = gpio_ch; + + if(init_read) this->last_read = (uint8_t)DIO_HIGH; + else this->last_read = (uint8_t)DIO_LOW; + + this->write(DIO_HIZ); +} + +board::DigitalIO::~DigitalIO(void) +{ + this->write(DIO_HIZ); +} + +uint8_t board::DigitalIO::read(void) +{ + uint8_t lvl = mcu::gpio_read(this->gpio_ch); + if(lvl>0) this->last_read = (uint8_t)DIO_HIGH; + else this->last_read = (uint8_t)DIO_LOW; + + return this->last_read; +} + +void board::DigitalIO::write(int8_t level) +{ + if(level > 0) + { + this->last_set = DIO_HIGH; + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); + } + else if(level < 0) + { + this->last_set = DIO_HIZ; + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIZ); + } + else + { + this->last_set = DIO_LOW; + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); + } +} + +uint8_t board::DigitalIO::is_io_match(void) +{ + if(this->last_set == DIO_HIZ) return 1; + + uint8_t read_lvl = this->read(); + + if(read_lvl == (uint8_t)this->last_set) return 1; + else return 0; +} + +int8_t board::DigitalIO::get_set_level(void) +{ + return this->last_set; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/dio.h b/firmware/src/board/dio.h new file mode 100644 index 0000000..96585f4 --- /dev/null +++ b/firmware/src/board/dio.h @@ -0,0 +1,39 @@ +#ifndef DIGITAL_OUT_H_ +#define DIGITAL_OUT_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +const int8_t DIO_LOW = 0; +const int8_t DIO_HIGH = 1; +const int8_t DIO_HIZ = -1; + +class DigitalIO +{ + protected: + uint8_t gpio_ch; + int8_t last_set; + + public: + DigitalIO(uint8_t gpio_ch, uint8_t init_read); + ~DigitalIO(void); + + uint8_t last_read; + + uint8_t read(void); + void write(int8_t level); + uint8_t is_io_match(void); + int8_t get_set_level(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DIGITAL_OUT_H_ */ \ No newline at end of file diff --git a/firmware/src/board/halfbridge.cpp b/firmware/src/board/halfbridge.cpp new file mode 100644 index 0000000..6ede670 --- /dev/null +++ b/firmware/src/board/halfbridge.cpp @@ -0,0 +1,68 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "halfbridge.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc) +{ + this->pwm_ch = hs_pwm_ch; + this->gpio_ch = ls_gpio_ch; + + if(max_dc>100) this->max_dc = 100; + else this->max_dc = max_dc; + this->disable(); +} + +board::Hafbridge::~Hafbridge(void) +{ + this->last_duty = 0; + this->disable(); +} + +void board::Hafbridge::write(uint8_t duty) +{ + // Limit duty + if(duty > this->max_dc) duty = this->max_dc; + this->last_duty = duty; + + if(this->enabled == 0) return; + + // Convert percent to 16b duty cycle + uint16_t dc = util::percent_to_16b(duty); + // Set PWM + mcu::pwm_write(this->pwm_ch, dc); +} + +void board::Hafbridge::enable(void) +{ + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); + this->enabled = 1; + this->write(this->last_duty); +} + +void board::Hafbridge::disable(void) +{ + mcu::pwm_write(this->pwm_ch, 0); + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); + this->enabled = 0; +} + +uint8_t board::Hafbridge::get_set_duty(void) +{ + return this->last_duty; +} + +uint8_t board::Hafbridge::is_enabled(void) +{ + return this->enabled; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/halfbridge.h b/firmware/src/board/halfbridge.h new file mode 100644 index 0000000..ef6414f --- /dev/null +++ b/firmware/src/board/halfbridge.h @@ -0,0 +1,37 @@ +#ifndef HALFBRIDGE_H_ +#define HALFBRIDGE_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +class Hafbridge +{ + protected: + uint8_t pwm_ch; + uint8_t gpio_ch; + uint8_t last_duty; + uint8_t enabled; + uint8_t max_dc; + + public: + Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc); + ~Hafbridge(void); + + void write(uint8_t duty); + void enable(void); + void disable(void); + uint8_t get_set_duty(void); + uint8_t is_enabled(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* HALFBRIDGE_H_ */ \ No newline at end of file diff --git a/firmware/src/board/hvdin.cpp b/firmware/src/board/hvdin.cpp new file mode 100644 index 0000000..8757efd --- /dev/null +++ b/firmware/src/board/hvdin.cpp @@ -0,0 +1,37 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "hvdin.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::HVDigitalIn::HVDigitalIn(uint8_t gpio_ch, uint8_t init_read) +{ + this->gpio_ch = gpio_ch; + + if(init_read) this->last_read = HVDIN_HIGH; + else this->last_read = HVDIN_LOW; +} + +board::HVDigitalIn::~HVDigitalIn(void) +{ + return; +} + +uint8_t board::HVDigitalIn::read(void) +{ + // Auto inverts level to match board external connectors + uint8_t lvl = mcu::gpio_read(this->gpio_ch); + if(lvl>0) this->last_read = HVDIN_LOW; + else this->last_read = HVDIN_HIGH; + + return this->last_read; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/hvdin.h b/firmware/src/board/hvdin.h new file mode 100644 index 0000000..b0e6244 --- /dev/null +++ b/firmware/src/board/hvdin.h @@ -0,0 +1,34 @@ +#ifndef HV_DIN_H_ +#define HV_DIN_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +const uint8_t HVDIN_LOW = 0; +const uint8_t HVDIN_HIGH = 1; + +class HVDigitalIn +{ + protected: + uint8_t gpio_ch; + + public: + HVDigitalIn(uint8_t gpio_ch, uint8_t init_read); + ~HVDigitalIn(void); + + uint8_t last_read; + + uint8_t read(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* HV_DIN_H_ */ \ No newline at end of file diff --git a/firmware/src/board/mcu/mcu_hal.h b/firmware/src/board/mcu/mcu_hal.h new file mode 100644 index 0000000..42a6495 --- /dev/null +++ b/firmware/src/board/mcu/mcu_hal.h @@ -0,0 +1,125 @@ +#ifndef MCU_HAL_H_ +#define MCU_HAL_H_ + +/**** Includes ****/ +#include + +namespace mcu { + +/**** Public definitions ****/ +/* +GPIO0 Down +GPIO1 Up +GPIO2 Mode +GPIO3 Handbrake +GPIO4 Brakes +GPIO5 Dimm +GPIO6 LED0 +GPIO7 LED1 +GPIO8 LED2 +GPIO9 LED3 +GPIO10 LED4 +GPIO11 LED5 +GPIO12 DCCD Enable +GPIO13 Handbrake pull +GPIO14 Speed pull +GPIO15 DCCD PWM +GPIO16 LED PWM + +ADC0 Output current +ADC1 Output voltage +ADC2 Battery current +ADC3 Battery voltage +ADC4 Potentiometer +ADC5 Mode +ADC8 MCU temperature +ADC14 MCU internal reference +ADC15 MCU ground +*/ + +const uint8_t GPIO0 = 0; //PC5 Mode +const uint8_t GPIO1 = 1; //PC4 Pot +const uint8_t GPIO2 = 2; //PE1 Down +const uint8_t GPIO3 = 3; //PE3 Up +const uint8_t GPIO4 = 4; //PD7 Dimm +const uint8_t GPIO5 = 5; //PB7 Brakes +const uint8_t GPIO6 = 6; //PB6 Handbrake +const uint8_t GPIO7 = 7; //PB5 Handbrake pull +const uint8_t GPIO8 = 8; //PD6 Speed pull +const uint8_t GPIO9 = 9; //PD0 LED0 +const uint8_t GPIO10 = 10; //PD1 LED1 +const uint8_t GPIO11 = 11; //PD2 LED2 +const uint8_t GPIO12 = 12; //PD3 LED3 +const uint8_t GPIO13 = 13; //PD4 LED4 +const uint8_t GPIO14 = 14; //PD5 LED5 +const uint8_t GPIO15 = 15; //PB0 DCCD Enable +const uint8_t GPIO16 = 16; //PB1 DCCD PWM +const uint8_t GPIO17 = 17; //PB2 LED PWM + +const uint8_t LEVEL_LOW = 0; +const uint8_t LEVEL_HIGH = 1; +const int8_t LEVEL_HIZ = -1; + +const uint8_t ADC0 = 0; //Output current +const uint8_t ADC1 = 1; //Output voltage +const uint8_t ADC2 = 2; //Battery voltage +const uint8_t ADC3 = 3; //Battery current +const uint8_t ADC4 = 4; //Potentiometer +const uint8_t ADC5 = 5; //Mode +const uint8_t ADC8 = 8; //MCU temperature +const uint8_t ADC14 = 14; //MCU internal reference +const uint8_t ADC15 = 15; //MCU ground + +const uint8_t PWM0 = 0; //DCCD +const uint8_t PWM1 = 1; //LED + +//ADC definitions +typedef enum { + ADC_DIV2 = 0x01, + ADC_DIV4 = 0x02, + ADC_DIV8 = 0x03, + ADC_DIV16 = 0x04, + ADC_DIV32 = 0x05, + ADC_DIV64 = 0x06, + ADC_DIV128 = 0x07 +} adcClkDiv_t; + +//Timer definitions +typedef enum { + TIM_DIV1 = 0x01, + TIM_DIV8 = 0x02, + TIM_DIV64 = 0x03, + TIM_DIV256 = 0x04, + TIM_DIV1024 = 0x05 +} timerClkDiv_t; + +typedef struct { + adcClkDiv_t adc_clk; + timerClkDiv_t pwm_clk; + uint16_t pwm_top; + uint8_t pwm_ch1_en; +} startupCfg_t; + +/**** Public function declarations ****/ +void startup(startupCfg_t* hwCfg); + +uint8_t gpio_read(uint8_t ch); +void gpio_write(uint8_t ch, int8_t lvl); +void gpio_write_pull(uint8_t ch, int8_t lvl); + +uint16_t adc_read(uint8_t ch); + +void pwm_write(uint8_t ch, uint16_t dc); +uint16_t pwm_read(uint8_t ch); + +uint8_t eeprom_read8b(uint16_t address); +uint16_t eeprom_read16b(uint16_t address); +uint32_t eeprom_read32b(uint16_t address); + +void eeprom_write8b(uint16_t address, uint8_t value); +void eeprom_write16b(uint16_t address, uint16_t value); +void eeprom_write32b(uint16_t address, uint32_t value); + +} //namespace + +#endif /* MCU_HAL_H_ */ diff --git a/firmware/src/board/mcu/mcu_hal_r8.cpp b/firmware/src/board/mcu/mcu_hal_r8.cpp new file mode 100644 index 0000000..6b2d1de --- /dev/null +++ b/firmware/src/board/mcu/mcu_hal_r8.cpp @@ -0,0 +1,464 @@ +/**** Includes ****/ +#include +#include +#include "mcu_hal.h" + +using namespace mcu; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +static uint8_t gpio_read_level(uint8_t pin_reg, uint8_t mask); +static void pwm_write_ocx(uint8_t ch, uint16_t value); +static uint16_t pwm_read_ocx(uint8_t ch); + +/**** Public function definitions ****/ +void mcu::startup(startupCfg_t* hwCfg) +{ + // Fail-safe GPIO init + PORTB = 0xF8; // Set PORTB pull-ups + DDRB = 0x00; // Set all as inputs + + PORTC = 0x40; // Set PORTC pull-ups + DDRC = 0x00; // Set all as inputs + + PORTD = 0x80; // Set PORTD pull-ups + DDRD = 0x00; // Set all as inputs + + PORTE = 0x0A; // Set PORTE pull-ups + DDRE = 0x00; // Set all as inputs + + // Half-bridge related pins + PORTB &= ~0x03; //Set low + DDRB |= 0x03; //Set as output + + // Common OD PWM pin + if(hwCfg->pwm_ch1_en) PORTB &= ~0x04; //Set low + else PORTB |= 0x04; //Set high + DDRB |= 0x04; //Set as output + + // OD control pins + PORTD &= ~0x3F; //Set low (off) + DDRD |= 0x3F; //Set as outputs + + // Handbrake pull-up pin + PORTB |= 0x20; //Set high + DDRB |= 0x20; //Set as output + + // Handbrake and brakes pins + PORTB |= 0xC0; //Set pull-up on + DDRB &= ~0xC0; //Set as inputs + + // Dimm + PORTD |= 0x80; //Set pull-up on + DDRD &= ~0x80; //Set as input + + // Up and Down + PORTE |= 0x0A; //Set pull-up on + DDRE &= ~0x0A; //Set as inputs + + // Internal ADC inputs + PORTC &= ~0x0F; //Pull-up off + DDRC &= ~0x0F; //Set as inputs + + // Potentiometer & Mode + PORTC &= ~0x30; //Pull-up off + DDRC &= ~0x30; //Set as inputs + + //ADC configuration + PRR0 &= ~0x01; //Enable ADC power + DIDR0 |= 0x0F; //Disable digital inputs, ADC0-ADC3 + + ADMUX = 0x40; //Set AVCC reference, Right adjust + ADCSRA = 0x00; //ADC Disabled, Single conversion, no IT + ADCSRA |= (uint8_t)hwCfg->adc_clk; + ADCSRB = 0x00; //no trigger input + + ADCSRA |= 0x80; //Enable ADC + + //DCCD and LED PWM configuration + PRR0 &= ~0x80; //Enable Timer1 power + TCCR1A = 0xC2; //Connect OC1A, inverted mode + if(hwCfg->pwm_ch1_en) TCCR1A |= 0x30; //Connect OC1B, inverted mode + TCCR1B = 0x18; //PWM, Phase & Frequency Correct ICR1 top, no clock, WGM:0xE + TCCR1C = 0x00; + TCNT1 = 0x0000; + OCR1A = 0xFFFF; + OCR1B = 0xFFFF; + ICR1 = hwCfg->pwm_top; + TIMSK1 = 0x00; //No interrupts + TIFR1 = 0x00; //Clear all flags + + uint8_t tim1_prescaler = (uint8_t)hwCfg->pwm_clk; + TCCR1B |= tim1_prescaler; //Enable timer +} + +// ADC Interface functions +uint16_t mcu::adc_read(uint8_t ch) +{ + //check if ADC is enabled + if(!(ADCSRA&0x80)) return 0xFFFF; + + //Safe guard mux + if(ch > 15) return 0xFFFF; + // Not available channels + if((ch > 8) && (ch<14)) return 0xFFFF; + + ADMUX &= ~0x0F; + ADMUX |= ch; + ADCSRA |= 0x40; + while(ADCSRA&0x40); //wait to finish + return ADC; +} + +// PWM Timer Interface functions +void mcu::pwm_write(uint8_t ch, uint16_t dc) +{ + dc = 0xFFFF - dc; + + // Calculate value as % of TOP + uint32_t top = (uint32_t)ICR1; + uint32_t temp = (uint32_t)dc * top; + temp = temp/0x0000FFFF; + + //Limit temp + if(temp>0x0000FFFF) temp = 0x0000FFFF; + uint16_t ocrx = (uint16_t)temp; + + // Write register + pwm_write_ocx(ch, ocrx); +} + +uint16_t mcu::pwm_read(uint8_t ch) +{ + uint16_t ocrx = pwm_read_ocx(ch); + + // Check easy answers + if(ocrx == 0) return 0; + if(ocrx >= ICR1) return 0xFFFF; + + // Calculate + uint32_t top = (uint32_t)ICR1; + uint32_t temp = (uint32_t)ocrx * 0xFFFF; + temp = temp/top; + + //Limit temp + if(temp>0x0000FFFF) return 0xFFFF; + return (uint16_t)temp; +} + +uint8_t mcu::gpio_read(uint8_t ch) +{ + switch(ch) + { + case GPIO0: // Mode DIN1 + return gpio_read_level(PINC,0x20); + + case GPIO1: // Pot DIN2 + return gpio_read_level(PINC,0x10); + + case GPIO2: // Down DIN3 + return gpio_read_level(PINE,0x02); + + case GPIO3: // Up DIN4 + return gpio_read_level(PINE,0x08); + + case GPIO4: // Dimm DIN5 + return gpio_read_level(PIND,0x80); + + case GPIO5: // Brakes DIN6 + return gpio_read_level(PINB,0x80); + + case GPIO6: // Handbrake DIN7 + return gpio_read_level(PINB,0x40); + + case GPIO7: // Handbrake pull DIN8 + return gpio_read_level(PINB,0x20); + + case GPIO8: // Speed-pull + return gpio_read_level(PIND,0x40); + + case GPIO9: // LED 0 + return gpio_read_level(PIND,0x01); + + case GPIO10: // LED 1 + return gpio_read_level(PIND,0x02); + + case GPIO11: // LED 2 + return gpio_read_level(PIND,0x04); + + case GPIO12: // LED 3 + return gpio_read_level(PIND,0x08); + + case GPIO13: // LED 4 + return gpio_read_level(PIND,0x10); + + case GPIO14: // LED 5 + return gpio_read_level(PIND,0x20); + + case GPIO15: // DCCD Enable + return gpio_read_level(PINB,0x01); + + case GPIO16: // DCCD PWM + return gpio_read_level(PINB,0x02); + + case GPIO17: // LED PWM + return gpio_read_level(PINB,0x04); + + default: + return 0; + } +} + +void mcu::gpio_write(uint8_t ch, int8_t lvl) +{ + switch(ch) + { + case GPIO0: // Mode DIN1 + if(lvl>0) + { + PORTC |= 0x20; + DDRC |= 0x20; + } + else if(lvl<0) + { + DDRC &= ~0x20; + PORTC &= ~0x20; + } + else + { + PORTC &= ~0x20; + DDRC |= 0x20; + } + return; + + case GPIO1: // Pot DIN2 + if(lvl>0) + { + PORTC |= 0x10; + DDRC |= 0x10; + } + else if(lvl<0) + { + DDRC &= ~0x10; + PORTC &= ~0x10; + } + else + { + PORTC &= ~0x10; + DDRC |= 0x10; + } + return; + + case GPIO2: // Down DIN3 + if(lvl>0) + { + PORTE |= 0x02; + DDRE |= 0x02; + } + else if(lvl<0) + { + DDRE &= ~0x02; + PORTE &= ~0x02; + } + else + { + PORTE &= ~0x02; + DDRE |= 0x02; + } + return; + + case GPIO3: // Up DIN4 + if(lvl>0) + { + PORTE |= 0x08; + DDRE |= 0x08; + } + else if(lvl<0) + { + DDRE &= ~0x08; + PORTE &= ~0x08; + } + else + { + PORTE &= ~0x08; + DDRE |= 0x08; + } + return; + + case GPIO7: // Handbrake pull DIN + if(lvl>0) + { + PORTB |= 0x20; + DDRB |= 0x20; + } + else if(lvl<0) + { + DDRB &= ~0x20; + PORTB &= ~0x20; + } + else + { + PORTB &= ~0x20; + DDRB |= 0x20; + } + return; + + case GPIO8: // Speed-pull + if(lvl>0) PORTD |= 0x40; + else PORTD &= ~0x40; + return; + + case GPIO9: // LED 0 + if(lvl>0) PORTD |= 0x01; + else PORTD &= ~0x01; + return; + + case GPIO10: // LED 1 + if(lvl>0) PORTD |= 0x02; + else PORTD &= ~0x02; + return; + + case GPIO11: // LED 2 + if(lvl>0) PORTD |= 0x04; + else PORTD &= ~0x04; + return; + + case GPIO12: // LED 3 + if(lvl>0) PORTD |= 0x08; + else PORTD &= ~0x08; + return; + + case GPIO13: // LED 4 + if(lvl>0) PORTD |= 0x10; + else PORTD &= ~0x10; + return; + + case GPIO14: // LED 5 + if(lvl>0) PORTD |= 0x20; + else PORTD &= ~0x20; + return; + + case GPIO15: // DCCD Enable + if(lvl>0) PORTB |= 0x01; + else PORTB &= ~0x01; + return; + + default: + return; + } +} + +void mcu::gpio_write_pull(uint8_t ch, int8_t lvl) +{ + switch(ch) + { + case GPIO0: // Mode DIN1 + if(lvl>0) PORTC |= 0x20; + else PORTC &= ~0x20; + return; + + case GPIO1: // Pot DIN2 + if(lvl>0) PORTC |= 0x10; + else PORTC &= ~0x10; + return; + + case GPIO2: // Down DIN3 + if(lvl>0) PORTE |= 0x02; + else PORTE &= ~0x02; + return; + + case GPIO3: // Up DIN4 + if(lvl>0) PORTE |= 0x08; + else PORTE &= ~0x08; + return; + + case GPIO4: // Dimm + if(lvl>0) PORTD |= 0x80; + else PORTD &= ~0x80; + return; + + case GPIO5: // Brakes + if(lvl>0) PORTB |= 0x80; + else PORTB &= ~0x80; + return; + + case GPIO6: // Handbrake + if(lvl>0) PORTB |= 0x40; + else PORTB &= ~0x40; + return; + + default: + return; + } +} + +uint8_t mcu::eeprom_read8b(uint16_t address) +{ + return eeprom_read_byte((uint8_t*)address); +} + +uint16_t mcu::eeprom_read16b(uint16_t address) +{ + return eeprom_read_word((uint16_t*)address); +} + +uint32_t mcu::eeprom_read32b(uint16_t address) +{ + return eeprom_read_dword((uint32_t*)address); +} + +void mcu::eeprom_write8b(uint16_t address, uint8_t value) +{ + eeprom_write_byte((uint8_t*)address, value); +} + +void mcu::eeprom_write16b(uint16_t address, uint16_t value) +{ + eeprom_write_word((uint16_t*)address, value); +} + +void mcu::eeprom_write32b(uint16_t address, uint32_t value) +{ + eeprom_write_dword((uint32_t*)address, value); +} + +/**** Private function definitions ****/ +static uint8_t gpio_read_level(uint8_t pin_reg, uint8_t mask) +{ + if(pin_reg&mask) return LEVEL_HIGH; + else return LEVEL_LOW; +} + +static void pwm_write_ocx(uint8_t ch, uint16_t value) +{ + switch(ch) + { + case PWM0: + OCR1A = value; + return; + + case PWM1: + OCR1B = value; + return; + + default: + return; + } +} + +static uint16_t pwm_read_ocx(uint8_t ch) +{ + switch(ch) + { + case PWM0: + return OCR1A; + + case PWM1: + return OCR1B ; + + default: + return 0x0000; + } +} diff --git a/firmware/src/board/od_com.cpp b/firmware/src/board/od_com.cpp new file mode 100644 index 0000000..634b622 --- /dev/null +++ b/firmware/src/board/od_com.cpp @@ -0,0 +1,40 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "od_com.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::ODCommon::ODCommon(uint8_t pwm_ch) +{ + this->pwm_ch = pwm_ch; + this->write(0); +} + +board::ODCommon::~ODCommon(void) +{ + this->write(0); +} + +void board::ODCommon::write(uint8_t duty) +{ + // Convert percent to 16b duty cycle + uint16_t dc = util::percent_to_16b(duty); + + // Set PWM + mcu::pwm_write(this->pwm_ch, dc); + this->last_duty = duty; +} + +uint8_t board::ODCommon::get_set_duty(void) +{ + return this->last_duty; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/od_com.h b/firmware/src/board/od_com.h new file mode 100644 index 0000000..f9fa3cd --- /dev/null +++ b/firmware/src/board/od_com.h @@ -0,0 +1,31 @@ +#ifndef OD_COMMON_H_ +#define OD_COMMON_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +class ODCommon +{ + protected: + uint8_t pwm_ch; + uint8_t last_duty; + + public: + ODCommon(uint8_t pwm_ch); + ~ODCommon(void); + + void write(uint8_t duty); + uint8_t get_set_duty(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* OD_COMMON_H_ */ \ No newline at end of file diff --git a/firmware/src/board/odout.cpp b/firmware/src/board/odout.cpp new file mode 100644 index 0000000..a933e4b --- /dev/null +++ b/firmware/src/board/odout.cpp @@ -0,0 +1,44 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "odout.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::OpenDrainOut::OpenDrainOut(uint8_t gpio_ch) +{ + this->gpio_ch = gpio_ch; + this->write(OD_OFF); +} + +board::OpenDrainOut::~OpenDrainOut(void) +{ + this->write(OD_OFF); +} + +void board::OpenDrainOut::write(uint8_t state) +{ + if(state) + { + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); + this->last_set = OD_ON; + } + else + { + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); + this->last_set = OD_OFF; + } +} + +uint8_t board::OpenDrainOut::get_set_state(void) +{ + return this->last_set; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/odout.h b/firmware/src/board/odout.h new file mode 100644 index 0000000..3e6b80e --- /dev/null +++ b/firmware/src/board/odout.h @@ -0,0 +1,34 @@ +#ifndef OD_OUT_H_ +#define OD_OUT_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +const uint8_t OD_OFF = 0; +const uint8_t OD_ON = 1; + +class OpenDrainOut +{ + protected: + uint8_t gpio_ch; + uint8_t last_set; + + public: + OpenDrainOut(uint8_t gpio_ch); + ~OpenDrainOut(void); + + void write(uint8_t state); + uint8_t get_set_state(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* OD_OUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/mcu/mcu_hal.h b/firmware/src/hw/mcu/mcu_hal.h deleted file mode 100644 index 468975b..0000000 --- a/firmware/src/hw/mcu/mcu_hal.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef UDCCD_R8_BSP_H_ -#define UDCCD_R8_BSP_H_ - -/**** Includes ****/ -#include -#include "../common/level.h" - -/**** Public definitions ****/ -//ADC definitions -typedef enum { - ADC_ICOIL = 0x00, - ADC_UCOIL = 0x01, - ADC_UBAT = 0x02, - ADC_IBAT = 0x03, - ADC_POT = 0x04, - ADC_MODE = 0x05, - ADC_TEMP = 0x08, - ADC_INTREF = 0x0E, - ADC_GND = 0x0F -} adcCh_t; - -typedef enum { - ADC_DIV2 = 0x01, - ADC_DIV4 = 0x02, - ADC_DIV8 = 0x03, - ADC_DIV16 = 0x04, - ADC_DIV32 = 0x05, - ADC_DIV64 = 0x06, - ADC_DIV128 = 0x07 -} adcDiv_t; - -//Timer definitions -typedef enum { - TIM_DIV1 = 0x01, - TIM_DIV8 = 0x02, - TIM_DIV64 = 0x03, - TIM_DIV256 = 0x04, - TIM_DIV1024 = 0x05 -} timerDiv_t; - -typedef enum { - PWM_COIL = 'A', - PWM_LED = 'B' -} pwmCh_t; - -typedef enum { - SPEED_1 = 3, - SPEED_0 = 4 -} speedCh_t; - -typedef struct { - adcDiv_t adc_clk_prescaler; - uint8_t adc_auto_wake; - timerDiv_t pwm_timer_prescaler; - uint16_t pwm_timer_top; - timerDiv_t freq_timer_prescaler; - uint16_t uart_prescaler; - uint8_t systick_timer_top; - timerDiv_t systick_timer_prescaler; - uint8_t disable_unused; - uint8_t en_watchdog; -} hwConfig_t; - -/**** Public function declarations ****/ -void HAL_Init_Min(hwConfig_t* hwCfg); -void HAL_Init_Extra(hwConfig_t* hwCfg); - -level_t HAL_ReadLvl_Handbrake(void); -level_t HAL_ReadLvl_Brake(void); -level_t HAL_ReadLvl_Dimm(void); -level_t HAL_ReadLvl_BtnUp(void); -level_t HAL_ReadLvl_BtnDown(void); -level_t HAL_ReadLvl_BtnMode(void); -level_t HAL_ReadLvl_HandbrakePull(void); -level_t HAL_ReadLvl_CoilLow(void); -level_t HAL_ReadLvl_CoilHigh(void); -level_t HAL_ReadLvl_LedsPwm(void); -level_t HAL_ReadLvl_Speed0(void); -level_t HAL_ReadLvl_Speed1(void); -level_t HAL_ReadLvl_SpeedPull(void); - -void HAL_SetPull_Handbrake(level_t lvl); -void HAL_SetPull_Speed(level_t lvl); - -void HAL_ADC_Wake(void); -void HAL_ADC_Sleep(void); -uint16_t HAL_ADC_Read(adcCh_t ch); - -void HAL_Coil_SetLowSide(uint8_t on); -void HAL_Coil_SetPWM(uint8_t percent); -void HAL_Coil_SetPWM16b(uint16_t value); - -void HAL_LEDS_Set(uint8_t image); -uint8_t HAL_LEDS_Get(void); -void HAL_LEDS_SetPWM(uint8_t percent); - -void HAL_PWM_Wake(void); -void HAL_PWM_Sleep(void); -void HAL_PWM_SetDuty16b(pwmCh_t ch, uint16_t value); -void HAL_PWM_SetDuty100(pwmCh_t ch, uint8_t percent); - -#endif /* UDCCD_R8_BSP_H_ */ diff --git a/firmware/src/hw/mcu/mcu_r8_hal.cpp b/firmware/src/hw/mcu/mcu_r8_hal.cpp deleted file mode 100644 index fb28bcb..0000000 --- a/firmware/src/hw/mcu/mcu_r8_hal.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/**** Includes ****/ -#include -#include "udccd_hal.h" - -/**** Private variables ****/ -static uint8_t tim0_prescaler = 0x00; -static uint8_t tim1_prescaler = 0x00; -static uint8_t tim3_prescaler = 0x00; -static uint8_t tim4_prescaler = 0x00; - -/**** Private function declarations ****/ -static void PWM_SetOCx(pwmCh_t ch, uint16_t value); - -/**** Public function definitions ****/ -void HAL_Init_Min(hwConfig_t* hwCfg) -{ - // PIN Configuration - //DCCD Enable and PWM - PORTB &= ~0x03; //Set low - DDRB |= 0x03; //Set as outputs - - //LED PWM - PORTB &= ~0x04; //Set low - DDRB |= 0x04; //Set as output - - //UART TX - PORTB |= 0x18; //Set high / pull-up on - DDRB |= 0x08; //Set as output - DDRB &= ~0x10; //Set as input - - //Handbrake pull-up - PORTB |= 0x20; //Set high - DDRB |= 0x20; //Set as output - - //Handbrake and Brake inputs - PORTB |= 0xC0; //Pull-up on - DDRB &= ~0xC0; //Set as input - - // ADC inputs - PORTC &= ~0x3F; //Pull-up off - DDRC &= ~0x3F; //Set as inputs - - // Reset - PORTC |= 0x40; //Pull-up on - DDRC &= ~0x40; //Set as input - - // LED control - PORTD &= ~0x3F; //Set low - DDRD |= 0x3F; //Set as outputs - - // Speed pull - PORTD &= ~0x40; //Set low - DDRD |= 0x40; //Set as outputs - - // Dim input - PORTD |= 0x80; //Set pull-up on - DDRD &= ~0x80; //Set as input - - // Speed inputs - PORTE &= ~0x05; //Set pull-down - DDRE |= 0x05; //Set as output - - // Up/Down inputs - PORTE |= 0x0A; //Set pull-up on - DDRE &= ~0x0A; //Set as input - - - //ADC configuration - PRR0 &= ~0x01; //Enable ADC power - DIDR0 |= 0x1F; //Disable digital inputs, ADC0-ADC4 - - ADMUX = 0x40; //Set AVCC reference, Right adjust - ADCSRA = 0x00; //ADC Disabled, Single conversion, no IT - ADCSRA |= (uint8_t)hwCfg->adc_clk_prescaler; - ADCSRB = 0x00; //no trigger input - - if(hwCfg->adc_auto_wake) ADCSRA |= 0x80; //Enable ADC - else PRR0 |= 0x01; - - - //DCCD and LED PWM configuration - PRR0 &= ~0x80; //Enable Timer1 power - TCCR1A = 0xF2; //Connect OC1A and OC1B, normal logic - TCCR1B = 0x18; //PWM, Phase & Frequency Correct ICR1 top, no clock, WGM:0xE - TCCR1C = 0x00; - TCNT1 = 0x0000; - OCR1A = 0x0000; - OCR1B = 0x0000; - ICR1 = (hwCfg->pwm_timer_top); - TIMSK1 = 0x00; //No interrupts - TIFR1 = 0x00; //Clear all flags - - tim1_prescaler = (uint8_t)hwCfg->pwm_timer_prescaler; - TCCR1B |= tim1_prescaler; //Enable timer -} - -void HAL_Init_Extra(hwConfig_t* hwCfg) -{ - //Speed 1 input timer configuration - PRR1 &= ~0x01; //Enable Timer3 power - TCCR3A = 0x00; //OCx disconnected, WGM:0x0 - TCCR3B = 0x80; //ICP Noise filter, Falling edge, no clock - TCCR3C = 0x00; - TCNT3 = 0x0000; - OCR3A = 0x0000; - OCR3B = 0x0000; - ICR3 = 0x0000; - TIMSK3 = 0x00; - //TIMSK3 |= 0x21; //ICP and OVF interrupts - TIFR3 = 0x00; //Clear all flags - - tim3_prescaler = (uint8_t)hwCfg->freq_timer_prescaler; - TCCR3B |= tim3_prescaler; //Enable timer - - - //Speed 0 input timer configuration - PRR1 &= ~0x08; //Enable Timer4 power - TCCR4A = 0x00; //OCx disconnected, WGM:0x0 - TCCR4B = 0x80; //ICP Noise filter, Falling edge, no clock - TCCR4C = 0x00; - TCNT4 = 0x0000; - OCR4A = 0x0000; - OCR4B = 0x0000; - ICR4 = 0x0000; - TIMSK4 = 0x00; - //TIMSK4 |= 0x21; //ICP and OVF interrupts - TIFR4 = 0x00; //Clear all flags - - tim4_prescaler = (uint8_t)hwCfg->freq_timer_prescaler; - TCCR4B |= tim4_prescaler; //Enable timer - - - //UART1 configuration - PRR0 &= 0x10; //Enable UART1 power - UCSR1A = 0x00; //Clear flags, Single UART speed, Single processor mode - UCSR1B = 0x18; //Enable RX/TX hardware, 8bit char - //UCSR1B |= 0xC0; //Enable RX/TX interrupt, - UCSR1C = 0x06; ; //async, No parity, 1 stop bit, 8bit char, - UBRR1 = hwCfg->uart_prescaler; //UART baud rate select - - - //"Systick" timer configuration - PRR0 &= ~0x20; //Enable Timer0 power - TCCR0A = 0x02 ;//OC0x not connected, WGM 0x01-CTC OC0A TOP - TCCR0B = 0x00; //WGM 0x01-CTC, No clock - TIMSK0 = 0x00; - //TIMSK0 |= 0x01; //OVF interrupt enabled - TCNT0 = 0x00; - OCR0A = hwCfg->systick_timer_top; - OCR0B= 0x00; - TIFR0 = 0x00; //Reset all flags - - tim0_prescaler = (uint8_t)hwCfg->systick_timer_prescaler; - TCCR0B |= tim0_prescaler; - - - //Disabled not used power configuration - if(hwCfg->disable_unused) - { - //Disable power to not used peripherals - PRR0 |= 0xC6; //Disable TWI0, TIM2, SPI0, UART0 - PRR1 |= 0x34; //Disable TWI1, PRTC, SPI1 - } - - //Watchdog configuration - if(hwCfg->en_watchdog) - { - //watchdog timer setup - WDTCSR |= 0x10; //Change enable - WDTCSR |= 0x0D; //System reset mode, 0.5s period. - //use special instruction to reset watchdog timer - }; -} - - -// Handbrake input -level_t HAL_ReadLvl_Handbrake(void) -{ - if(PINB & 0x40) return HIGH; - else return LOW; -} - -// Brakes input -level_t HAL_ReadLvl_Brake(void) -{ - if(PINB & 0x80) return HIGH; - else return LOW; -} - -// Dimm input -level_t HAL_ReadLvl_Dimm(void) -{ - if(PIND & 0x80) return HIGH; - else return LOW; -} - -// UP button -level_t HAL_ReadLvl_BtnUp(void) -{ - if(PINE & 0x08) return HIGH; - else return LOW; -} - -// Down button -level_t HAL_ReadLvl_BtnDown(void) -{ - if(PINE & 0x02) return HIGH; - else return LOW; -} - -// Mode button -level_t HAL_ReadLvl_BtnMode(void) -{ - if(PINC & 0x20) return HIGH; - else return LOW; -} - -// Handbrake pull -level_t HAL_ReadLvl_HandbrakePull(void) -{ - if(PINB & 0x20) return HIGH; - else return LOW; -} - -// Coil driver control low -level_t HAL_ReadLvl_CoilLow(void) -{ - if(PINB & 0x01) return HIGH; - else return LOW; -} - -// Coil driver control high -level_t HAL_ReadLvl_CoilHigh(void) -{ - if(PINB & 0x02) return HIGH; - else return LOW; -} - -// LED PWM control -level_t HAL_ReadLvl_LedsPwm(void) -{ - if(PINB & 0x04) return HIGH; - else return LOW; -} - -// Speed 0 input pin -level_t HAL_ReadLvl_Speed0(void) -{ - if(PINE & 0x04) return HIGH; - else return LOW; -} - -// Speed 1 input pin -level_t HAL_ReadLvl_Speed1(void) -{ - if(PINE & 0x01) return HIGH; - else return LOW; -} - -// Speed common pull pin -level_t HAL_ReadLvl_SpeedPull(void) -{ - if(PIND & 0x40) return HIGH; - else return LOW; -} - - -// Set handbrake pull-up -void HAL_SetPull_Handbrake(level_t lvl) -{ - switch(lvl) - { - case HIGH: - PORTB |= 0x20; //Set high - DDRB |= 0x20; //Set as output - break; - - case LOW: - PORTB &= ~0x20; //Set low - DDRB |= 0x20; //Set as output - - default: - DDRB &= ~0x20; //Set as input - PORTB |= 0x20; //Set high - break; - } -} - -// Set speed inputs common pull -void HAL_SetPull_Speed(level_t lvl) -{ - switch(lvl) - { - case HIGH: - PORTD |= 0x40; //Set high - break; - - default: - PORTD &= ~0x40; //Set low - break; - } -} - - -// ADC Wake -void HAL_ADC_Wake(void) -{ - //Enable ADC power - PRR0 &= ~0x01; - //Enable ADC - ADCSRA |= 0x80; -} - -// ADC Sleep -void HAL_ADC_Sleep(void) -{ - //wait to finish - while(ADCSRA&0x40); - //Disable ADC - ADCSRA &= ~0x80; - //Disable ADC power - PRR0 |= 0x01; -} - -// ADC Read -uint16_t HAL_ADC_Read(adcCh_t ch) -{ - //check if ADC is enabled - if(!(ADCSRA&0x80)) return 0xFFFF; - - uint8_t mux = (uint8_t)ch; - //Safe guard mux - if(mux > 15) return 0xFFFF; - // Not available channels - if((mux > 8) && (mux<14)) return 0xFFFF; - - ADMUX &= ~0x0F; - ADMUX |= mux; - ADCSRA |= 0x40; - while(ADCSRA&0x40); //wait to finish - return ADC; -} - - -// Coil Driver Low Side control -void HAL_Coil_SetLowSide(uint8_t on) -{ - if(on) PORTB |= 0x01; - else PORTB &= ~0x01; -} - -void HAL_Coil_SetPWM(uint8_t percent) -{ - HAL_PWM_SetDuty100(PWM_COIL, percent); -} - -void HAL_Coil_SetPWM16b(uint16_t value) -{ - HAL_PWM_SetDuty16b(PWM_COIL, value); -} - - -// LED Display -void HAL_LEDS_Set(uint8_t image) -{ - //Read current PORTD pin6, pin7 - uint8_t keep = PORTD & 0xC0; - - //Safe guard display - image &= 0x3F; - - //Calculate new PORTD - keep |= image; - - //Set PORTD - PORTD = keep; -} - -uint8_t HAL_LEDS_Get(void) -{ - return (PIND & 0x3F); -} - -void HAL_LEDS_SetPWM(uint8_t percent) -{ - HAL_PWM_SetDuty100(PWM_LED, percent); -} - - -// PWM Direct functions -void HAL_PWM_Wake(void) -{ - //Enable Timer1 power - PRR0 &= ~0x80; - //Prepare Timer1 settings - TCNT1 = 0x0000; - OCR1A = 0x0000; - OCR1B = 0x0000; - //Enable clock - TCCR1B |= tim1_prescaler; //Enable timer -} - -void HAL_PWM_Sleep(void) -{ - // Turn off outputs - OCR1A = 0x0000; - OCR1B = 0x0000; - // Force timer to bottom - TCNT1 = (ICR1-1); - // Wait for outputs to be off - while((PINB&0x06)!=0x00) continue; - // Disable clock - TCCR1B &= ~0x07; - // Disable Timer1 power - PRR0 |= 0x80; -} - -void HAL_PWM_SetDuty16b(pwmCh_t ch, uint16_t value) -{ - value = 0xFFFF - value; - - uint32_t top = (uint32_t)ICR1; - uint32_t temp = (uint32_t)value * top; - temp = temp/0x0000FFFF; - //Limit temp - if(temp>0x0000FFFF) temp = 0x0000FFFF; - uint16_t ocrx = (uint16_t) temp; - - PWM_SetOCx(ch, ocrx); -} - -void HAL_PWM_SetDuty100(pwmCh_t ch, uint8_t percent) -{ - if(percent > 100) percent = 100; - percent = 100 - percent; - - uint32_t top = (uint32_t)ICR1; - uint32_t temp = (uint32_t)percent * top; - temp = temp/100; - //Limit temp - if(temp>0x0000FFFF) temp = 0x0000FFFF; - uint16_t ocrx = (uint16_t) temp; - - PWM_SetOCx(ch, ocrx); -} - -/**** Private function definitions ****/ -static void PWM_SetOCx(pwmCh_t ch, uint16_t value) -{ - switch(ch) - { - case PWM_COIL: - OCR1A = value; - return; - - case PWM_LED: - OCR1B = value; - return; - - default: - return; - } -} \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 77f92f9..16d5bba 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,18 +1,85 @@ -/* - * uDCCD.cpp - * - * Created: 12.03.2024 20:39:27 - * Author : User - */ +/**** Includes ****/ +#include "utils/utils.h" +#include "board/mcu/mcu_hal.h" +#include "board/ain.h" +#include "board/dio.h" +#include "board/hvdin.h" +#include "board/halfbridge.h" +#include "board/od_com.h" +#include "board/odout.h" -#include +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +static board::AnalogIn dccd_i(mcu::ADC0); +static board::AnalogIn dccd_u(mcu::ADC1); +static board::AnalogIn bat_u(mcu::ADC2); +static board::AnalogIn bat_i(mcu::ADC3); +static board::AnalogIn ain1(mcu::ADC5); +static board::AnalogIn ain2(mcu::ADC4); +static board::DigitalIO din1(mcu::GPIO0, board::DIO_HIGH); +static board::DigitalIO din2(mcu::GPIO1, board::DIO_HIGH); +static board::DigitalIO din3(mcu::GPIO2, board::DIO_HIGH); +static board::DigitalIO din4(mcu::GPIO3, board::DIO_HIGH); + +static board::HVDigitalIn hvdin1(mcu::GPIO4, board::HVDIN_LOW); +static board::HVDigitalIn hvdin2(mcu::GPIO5, board::HVDIN_LOW); +static board::HVDigitalIn hvdin3(mcu::GPIO6, board::HVDIN_LOW); + +/**** Private function declarations ****/ +/**** Public function definitions ****/ int main(void) { - /* Replace with your application code */ - while (1) - { - } + mcu::startupCfg_t mcu_cfg; + mcu_cfg.adc_clk = mcu::ADC_DIV2; + mcu_cfg.pwm_clk = mcu::TIM_DIV1; + mcu_cfg.pwm_top = 200; + mcu_cfg.pwm_ch1_en = 1; + + mcu::startup(&mcu_cfg); + + dccd_i.mul = 1; + dccd_i.div = 1; + dccd_i.offset = 0; + + dccd_u.mul = 1; + dccd_u.div = 1; + dccd_u.offset = 0; + + bat_u.mul = 1; + bat_u.div = 1; + bat_u.offset = 0; + + bat_i.mul = 1; + bat_i.div = 1; + bat_i.offset = 0; + + // Super loop + while(1) + { + dccd_i.read(); + dccd_u.read(); + bat_u.read(); + bat_i.read(); + ain1.read(); + ain2.read(); + + din1.read(); + din2.read(); + din3.read(); + din4.read(); + + hvdin1.read(); + hvdin2.read(); + hvdin3.read(); + + continue; // End of super loop + } + + // Escape the matrix + return 0; } +/**** Private function definitions ***/ diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index de25ed8..327eb2a 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -30,132 +30,191 @@ - - - - - - - - - + + + + + + + + + - -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" - True - True - True - True - False - True - True - - - NDEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Optimize for size (-Os) - True - True - True - True - True - - - NDEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Optimize for size (-Os) - True - True - True - - - libm - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - + -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" + True + True + True + True + False + True + True + + + NDEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize for size (-Os) + True + True + True + True + True + + + NDEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize for size (-Os) + True + True + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + - -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" - True - True - True - True - False - True - True - - - DEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Optimize debugging experience (-Og) - True - True - Default (-g2) - True - True - True - - - DEBUG - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Optimize debugging experience (-Og) - True - True - Default (-g2) - True - - - libm - - - - - %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ - - - Default (-Wa,-g) - + -mmcu=atmega328pb -B "%24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\gcc\dev\atmega328pb" + True + True + True + True + False + True + True + + + DEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize debugging experience (-Og) + True + True + Default (-g2) + True + True + True + + + DEBUG + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Optimize debugging experience (-Og) + True + True + Default (-g2) + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATmega_DFP\1.7.374\include\ + + + Default (-Wa,-g) + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + compile + + compile + + + compile + + + compile + + + compile + + + + + + \ No newline at end of file diff --git a/firmware/src/utils/interpolate.cpp b/firmware/src/utils/interpolate.cpp new file mode 100644 index 0000000..fee0cfc --- /dev/null +++ b/firmware/src/utils/interpolate.cpp @@ -0,0 +1,163 @@ +/**** Includes ****/ +#include "utils.h" +#include "interpolate.h" + +using namespace util; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +uint16_t util::interpolate_1d(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis) +{ + // validate axis length + if(len_axis==0) return 0; // Empty data set + if(len_axis==1) return y_values[0]; // Only one data point + + uint16_t y; + + uint8_t i = find_interval_end_index(x, x_axis, len_axis); + if(i==0) + { + //Less then start + y = y_values[0]; + } + else if(i==len_axis) + { + //More than end + y = y_values[len_axis-1]; + } + else + { + // Do interpolate + y = interpolate(x, x_axis[i-1], x_axis[i], y_values[i-1], y_values[i]); + } + + return y; +} + +uint16_t util::interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values) +{ + // validate axis length + if((len_x_axis==0)&&(len_y_axis==0)) return 0; // Empty data set + if((len_x_axis==1)&&(len_y_axis==1)) return z_values[0]; // Only one data point + + uint8_t ix = find_interval_end_index(x, x_axis, len_x_axis); + uint8_t iy = find_interval_end_index(y, y_axis, len_y_axis); + + // Check corners - easy answers + if((ix==0)&&(iy==0)) + { + return z_values[0]; //[0][0] [Y][X] + } + else if((ix==len_x_axis)&&(iy==0)) + { + return z_values[len_x_axis-1]; //[0][end] + } + else if((ix==0)&&(iy==len_y_axis)) + { + uint16_t i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); + return z_values[i]; //[end][0] + } + else if((ix==len_x_axis)&&(iy==len_y_axis)) + { + uint16_t i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); + return z_values[i]; //[end][end] + }; + + // Check boundaries - 1D interpolation + if(ix==0) + { + // On ix=0 line + uint16_t i = 0; + uint16_t z0 = z_values[i]; + i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); + uint16_t z1 = z_values[i]; + return interpolate(y, y_axis[0], y_axis[len_y_axis-1], z0, z1); + } + else if(ix==len_x_axis) + { + // On ix=END line + uint16_t i = len_x_axis-1; + uint16_t z0 = z_values[i]; + i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); + uint16_t z1 = z_values[i]; + return interpolate(y, y_axis[0], y_axis[len_y_axis-1], z0, z1); + } + else if(iy==0) + { + // On iy=0 line + uint16_t i = 0; + uint16_t z0 = z_values[i]; + i = len_x_axis-1; + uint16_t z1 = z_values[i]; + return interpolate(x, x_axis[0], x_axis[len_x_axis-1], z0, z1); + } + else if(iy==len_y_axis) + { + // On iy=END line + uint16_t i = index2d_to_index1d(0, len_y_axis-1, len_x_axis); + uint16_t z0 = z_values[i]; + i = index2d_to_index1d(len_x_axis-1, len_y_axis-1, len_x_axis); + uint16_t z1 = z_values[i]; + return interpolate(x, x_axis[0], x_axis[len_x_axis-1], z0, z1); + } + + // Do interpolation + // Get axis values + uint16_t x0 = x_axis[ix-1]; + uint16_t x1 = x_axis[ix]; + uint16_t y0 = y_axis[iy-1]; + uint16_t y1 = y_axis[iy]; + + // Do y0 line calculation + // Get z values at x0 and x1 points on y0 line + uint16_t i = index2d_to_index1d(ix-1, iy-1, len_x_axis); + uint16_t z0 = z_values[i]; + uint16_t z1 = z_values[i+1]; + // Interpolate z value on y0 line + uint16_t zy0 = interpolate(x, x0, x1, z0, z1); + + // Do y1 line calculation + // Get z values at x0 and x1 points on y1 line + i = index2d_to_index1d(ix-1, iy, len_x_axis); + z0 = z_values[i]; + z1 = z_values[i+1]; + // Interpolate z value on y0 line + uint16_t zy1 = interpolate(x, x0, x1, z0, z1); + + // Do calculation in y axis on xz line + return interpolate(y, y0, y1, zy0, zy1); +} + +uint16_t interpolate(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1) +{ + int32_t dy = (int32_t)y1 - (int32_t)y0; + int32_t dx = (int32_t)x1 - (int32_t)x0; + int32_t d = (int32_t)x - (int32_t)x0; + + int32_t y = dy * d; + y /= dx; + y += y0; + + return util::sat_cast(y); +} + +uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis) +{ + for(uint8_t i=0; i + +namespace util { + +/**** Public definitions ****/ +/**** Public function declarations ****/ +uint16_t interpolate_1d(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis); +uint16_t interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values); + +uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis); +uint16_t interpolate(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1); +uint16_t index2d_to_index1d(uint8_t ix, uint8_t iy, uint8_t len_x); + +#ifdef TESTING +#endif + +} //namespace + +#endif /* UTILS_INTERPOLATE_H_ */ \ No newline at end of file diff --git a/firmware/src/utils/utils.cpp b/firmware/src/utils/utils.cpp new file mode 100644 index 0000000..fae8eda --- /dev/null +++ b/firmware/src/utils/utils.cpp @@ -0,0 +1,137 @@ +/**** Includes ****/ +#include "utils.h" + +using namespace util; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +#ifndef TESTING +#endif + +/**** Public function definitions ****/ +uint8_t util::invert(uint8_t x) +{ + if(x!=0) return 0; + else return 1; +} + +uint8_t util::sat_add(uint8_t x, uint8_t y) +{ + uint8_t z = x + y; + // Check for overflow + if((z < x)||(z < y)) return 0xFF; + else return z; +} + +uint16_t util::sat_add(uint16_t x, uint16_t y) +{ + uint16_t z = x + y; + // Check for overflow + if((z < x)||(z < y)) return 0xFF; + else return z; +} + +uint32_t util::sat_add(uint32_t x, uint32_t y) + +{ + uint32_t z = x + y; + // Check for overflow + if((z < x)||(z < y)) return 0xFF; + else return z; +} + +uint8_t util::sat_subtract(uint8_t x, uint8_t y) +{ + uint8_t z = x - y; + // Check for underflow + if(z > x) return 0; + else return z; +} + +uint16_t util::sat_subtract(uint16_t x, uint16_t y) +{ + uint16_t z = x - y; + // Check for underflow + if(z > x) return 0; + else return z; +} + +uint32_t util::sat_subtract(uint32_t x, uint32_t y) +{ + uint32_t z = x - y; + // Check for underflow + if(z > x) return 0; + else return z; +} + +uint16_t util::sat_cast(uint32_t x) +{ + if(x > 0x0000FFFF) return 0xFFFF; + else return (uint16_t)x; +} + +uint16_t util::sat_cast(int32_t x) +{ + if(x < 0) return 0x0000; + else if(x > 0x0000FFFF) return 0xFFFF; + else return (uint16_t)x; +} + +uint16_t util::convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset) +{ + int32_t temp = (int32_t)raw; + + temp = temp * mul; + if(div>1) temp /= div; + temp += offset; + + return sat_cast(temp); +} + +uint16_t util::sat_mul_kilo(uint16_t xk, uint16_t yk) +{ + uint32_t temp = (uint32_t)xk * (uint32_t)yk; + temp /= 1000; + + return sat_cast(temp); +} + +uint16_t util::sat_div_kilo(uint16_t top, uint16_t bot) +{ + //Sanity check bot + if(bot==0) return 0xFFFF; //aka infinity + + uint32_t temp = (uint32_t)top * 1000; + temp /= (uint32_t)bot; + + return sat_cast(temp); +} + +uint16_t util::sat_ratio(uint16_t top, uint16_t bot) +{ + //Sanity check bot + if(bot==0) return 0xFFFF; //aka infinity + + //Easy option + if(top>=bot) return 0xFFFF; + + uint32_t temp = (uint32_t)top * 0x0000FFFF; + temp /= (uint32_t)bot; + + return sat_cast(temp); +} + +uint16_t util::percent_to_16b(uint8_t percent) +{ + uint32_t temp = (uint32_t)percent * 0x0000FFFF; + temp /= 100; + + // Limit to 16 bits + uint16_t pwm = sat_cast(temp); + + return pwm; +} + +/**** Private function definitions ****/ \ No newline at end of file diff --git a/firmware/src/utils/utils.h b/firmware/src/utils/utils.h new file mode 100644 index 0000000..d411a15 --- /dev/null +++ b/firmware/src/utils/utils.h @@ -0,0 +1,38 @@ +#ifndef UTILS_H_ +#define UTILS_H_ + +/**** Includes ****/ +#include + +namespace util { + +/**** Public definitions ****/ +/**** Public function declarations ****/ +uint8_t invert(uint8_t x); + +uint16_t sat_cast(uint32_t x); +uint16_t sat_cast(int32_t x); + +uint16_t convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset); +uint16_t sat_mul_kilo(uint16_t xk, uint16_t yk); +uint16_t sat_div_kilo(uint16_t top, uint16_t bot); +uint16_t sat_ratio(uint16_t top, uint16_t bot); +uint16_t percent_to_16b(uint8_t percent); + +uint8_t sat_add(uint8_t x, uint8_t y); +uint16_t sat_add(uint16_t x, uint16_t y); +uint32_t sat_add(uint32_t x, uint32_t y); + +uint8_t sat_subtract(uint8_t x, uint8_t y); +uint16_t sat_subtract(uint16_t x, uint16_t y); +uint32_t sat_subtract(uint32_t x, uint32_t y); + +uint16_t interpolate_1d(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis); +uint16_t interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values); + +#ifdef TESTING +#endif + +} //namespace + +#endif /* UTILS_H_ */ \ No newline at end of file -- 2.49.1 From 989d5a1f13e3f2edd49f1619c643862d4f2c0f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:44:41 +0300 Subject: [PATCH 03/35] Will redo this --- firmware/src/board/hvdin.cpp | 37 ----------------------------- firmware/src/board/hvdin.h | 34 --------------------------- firmware/src/board/od_com.cpp | 40 ------------------------------- firmware/src/board/od_com.h | 31 ------------------------ firmware/src/board/odout.cpp | 44 ----------------------------------- firmware/src/board/odout.h | 34 --------------------------- 6 files changed, 220 deletions(-) delete mode 100644 firmware/src/board/hvdin.cpp delete mode 100644 firmware/src/board/hvdin.h delete mode 100644 firmware/src/board/od_com.cpp delete mode 100644 firmware/src/board/od_com.h delete mode 100644 firmware/src/board/odout.cpp delete mode 100644 firmware/src/board/odout.h diff --git a/firmware/src/board/hvdin.cpp b/firmware/src/board/hvdin.cpp deleted file mode 100644 index 8757efd..0000000 --- a/firmware/src/board/hvdin.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "hvdin.h" - -using namespace board; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -board::HVDigitalIn::HVDigitalIn(uint8_t gpio_ch, uint8_t init_read) -{ - this->gpio_ch = gpio_ch; - - if(init_read) this->last_read = HVDIN_HIGH; - else this->last_read = HVDIN_LOW; -} - -board::HVDigitalIn::~HVDigitalIn(void) -{ - return; -} - -uint8_t board::HVDigitalIn::read(void) -{ - // Auto inverts level to match board external connectors - uint8_t lvl = mcu::gpio_read(this->gpio_ch); - if(lvl>0) this->last_read = HVDIN_LOW; - else this->last_read = HVDIN_HIGH; - - return this->last_read; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/board/hvdin.h b/firmware/src/board/hvdin.h deleted file mode 100644 index b0e6244..0000000 --- a/firmware/src/board/hvdin.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef HV_DIN_H_ -#define HV_DIN_H_ - -/**** Includes ****/ -#include - -namespace board { - -/**** Public definitions ****/ -const uint8_t HVDIN_LOW = 0; -const uint8_t HVDIN_HIGH = 1; - -class HVDigitalIn -{ - protected: - uint8_t gpio_ch; - - public: - HVDigitalIn(uint8_t gpio_ch, uint8_t init_read); - ~HVDigitalIn(void); - - uint8_t last_read; - - uint8_t read(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* HV_DIN_H_ */ \ No newline at end of file diff --git a/firmware/src/board/od_com.cpp b/firmware/src/board/od_com.cpp deleted file mode 100644 index 634b622..0000000 --- a/firmware/src/board/od_com.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "od_com.h" - -using namespace board; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -board::ODCommon::ODCommon(uint8_t pwm_ch) -{ - this->pwm_ch = pwm_ch; - this->write(0); -} - -board::ODCommon::~ODCommon(void) -{ - this->write(0); -} - -void board::ODCommon::write(uint8_t duty) -{ - // Convert percent to 16b duty cycle - uint16_t dc = util::percent_to_16b(duty); - - // Set PWM - mcu::pwm_write(this->pwm_ch, dc); - this->last_duty = duty; -} - -uint8_t board::ODCommon::get_set_duty(void) -{ - return this->last_duty; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/board/od_com.h b/firmware/src/board/od_com.h deleted file mode 100644 index f9fa3cd..0000000 --- a/firmware/src/board/od_com.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef OD_COMMON_H_ -#define OD_COMMON_H_ - -/**** Includes ****/ -#include - -namespace board { - -/**** Public definitions ****/ -class ODCommon -{ - protected: - uint8_t pwm_ch; - uint8_t last_duty; - - public: - ODCommon(uint8_t pwm_ch); - ~ODCommon(void); - - void write(uint8_t duty); - uint8_t get_set_duty(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* OD_COMMON_H_ */ \ No newline at end of file diff --git a/firmware/src/board/odout.cpp b/firmware/src/board/odout.cpp deleted file mode 100644 index a933e4b..0000000 --- a/firmware/src/board/odout.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "odout.h" - -using namespace board; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -board::OpenDrainOut::OpenDrainOut(uint8_t gpio_ch) -{ - this->gpio_ch = gpio_ch; - this->write(OD_OFF); -} - -board::OpenDrainOut::~OpenDrainOut(void) -{ - this->write(OD_OFF); -} - -void board::OpenDrainOut::write(uint8_t state) -{ - if(state) - { - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); - this->last_set = OD_ON; - } - else - { - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); - this->last_set = OD_OFF; - } -} - -uint8_t board::OpenDrainOut::get_set_state(void) -{ - return this->last_set; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/board/odout.h b/firmware/src/board/odout.h deleted file mode 100644 index 3e6b80e..0000000 --- a/firmware/src/board/odout.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef OD_OUT_H_ -#define OD_OUT_H_ - -/**** Includes ****/ -#include - -namespace board { - -/**** Public definitions ****/ -const uint8_t OD_OFF = 0; -const uint8_t OD_ON = 1; - -class OpenDrainOut -{ - protected: - uint8_t gpio_ch; - uint8_t last_set; - - public: - OpenDrainOut(uint8_t gpio_ch); - ~OpenDrainOut(void); - - void write(uint8_t state); - uint8_t get_set_state(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* OD_OUT_H_ */ \ No newline at end of file -- 2.49.1 From 0b9d6fa78095763022aa93da2ecb813d8a47bf1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:45:03 +0300 Subject: [PATCH 04/35] Created digital IO classes --- firmware/src/board/din.cpp | 40 ++++++++++++++++++++++++++++ firmware/src/board/din.h | 35 +++++++++++++++++++++++++ firmware/src/board/dio.cpp | 46 +++----------------------------- firmware/src/board/dio.h | 21 +++++---------- firmware/src/board/dout.cpp | 52 +++++++++++++++++++++++++++++++++++++ firmware/src/board/dout.h | 36 +++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 56 deletions(-) create mode 100644 firmware/src/board/din.cpp create mode 100644 firmware/src/board/din.h create mode 100644 firmware/src/board/dout.cpp create mode 100644 firmware/src/board/dout.h diff --git a/firmware/src/board/din.cpp b/firmware/src/board/din.cpp new file mode 100644 index 0000000..5a89808 --- /dev/null +++ b/firmware/src/board/din.cpp @@ -0,0 +1,40 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "din.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::DigitalIn::DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value) +{ + this->gpio_ch = gpio_ch; + this->invert = inverted; + + if(init_value) this->last_read = DIN_HIGH; + else this->last_read = DIN_LOW; +} + +board::DigitalIn::~DigitalIn(void) +{ + return; +} + +uint8_t board::DigitalIn::read(void) +{ + uint8_t lvl = mcu::gpio_read(this->gpio_ch); + + if(this->invert) lvl = util::invert(lvl); + + if(lvl>0) this->last_read = DIN_HIGH; + else this->last_read = DIN_LOW; + + return this->last_read; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/din.h b/firmware/src/board/din.h new file mode 100644 index 0000000..4b0776e --- /dev/null +++ b/firmware/src/board/din.h @@ -0,0 +1,35 @@ +#ifndef DIGITAL_INPUT_H_ +#define DIGITAL_INPUT_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +const uint8_t DIN_LOW = 0; +const uint8_t DIN_HIGH = 1; + +class DigitalIn +{ + protected: + uint8_t gpio_ch; + uint8_t invert; + + public: + DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value); + ~DigitalIn(void); + + uint8_t last_read; + + uint8_t read(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DIGITAL_INPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/board/dio.cpp b/firmware/src/board/dio.cpp index c70f4d0..9dc2934 100644 --- a/firmware/src/board/dio.cpp +++ b/firmware/src/board/dio.cpp @@ -11,52 +11,19 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_read) +board::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_value) : DigitalIn(gpio_ch, 0, init_value), DigitalOut(gpio_ch, 0) { - this->gpio_ch = gpio_ch; - - if(init_read) this->last_read = (uint8_t)DIO_HIGH; - else this->last_read = (uint8_t)DIO_LOW; - - this->write(DIO_HIZ); + return; } board::DigitalIO::~DigitalIO(void) { - this->write(DIO_HIZ); -} - -uint8_t board::DigitalIO::read(void) -{ - uint8_t lvl = mcu::gpio_read(this->gpio_ch); - if(lvl>0) this->last_read = (uint8_t)DIO_HIGH; - else this->last_read = (uint8_t)DIO_LOW; - - return this->last_read; -} - -void board::DigitalIO::write(int8_t level) -{ - if(level > 0) - { - this->last_set = DIO_HIGH; - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); - } - else if(level < 0) - { - this->last_set = DIO_HIZ; - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIZ); - } - else - { - this->last_set = DIO_LOW; - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); - } + this->write(DOUT_HIZ); } uint8_t board::DigitalIO::is_io_match(void) { - if(this->last_set == DIO_HIZ) return 1; + if(this->last_set == DOUT_HIZ) return 1; uint8_t read_lvl = this->read(); @@ -64,9 +31,4 @@ uint8_t board::DigitalIO::is_io_match(void) else return 0; } -int8_t board::DigitalIO::get_set_level(void) -{ - return this->last_set; -} - /**** Private function definitions ****/ diff --git a/firmware/src/board/dio.h b/firmware/src/board/dio.h index 96585f4..bddd90b 100644 --- a/firmware/src/board/dio.h +++ b/firmware/src/board/dio.h @@ -1,8 +1,10 @@ -#ifndef DIGITAL_OUT_H_ -#define DIGITAL_OUT_H_ +#ifndef DIGITAL_IO_H_ +#define DIGITAL_IO_H_ /**** Includes ****/ #include +#include "din.h" +#include "dout.h" namespace board { @@ -11,22 +13,13 @@ const int8_t DIO_LOW = 0; const int8_t DIO_HIGH = 1; const int8_t DIO_HIZ = -1; -class DigitalIO +class DigitalIO : public DigitalIn, public DigitalOut { - protected: - uint8_t gpio_ch; - int8_t last_set; - public: - DigitalIO(uint8_t gpio_ch, uint8_t init_read); + DigitalIO(uint8_t gpio_ch, uint8_t init_value); ~DigitalIO(void); - uint8_t last_read; - - uint8_t read(void); - void write(int8_t level); uint8_t is_io_match(void); - int8_t get_set_level(void); }; /**** Public function declarations ****/ @@ -36,4 +29,4 @@ class DigitalIO } //namespace -#endif /* DIGITAL_OUT_H_ */ \ No newline at end of file +#endif /* DIGITAL_IO_H_ */ \ No newline at end of file diff --git a/firmware/src/board/dout.cpp b/firmware/src/board/dout.cpp new file mode 100644 index 0000000..b2e6608 --- /dev/null +++ b/firmware/src/board/dout.cpp @@ -0,0 +1,52 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "dout.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::DigitalOut::DigitalOut(uint8_t gpio_ch, uint8_t inverted) +{ + this->gpio_ch = gpio_ch; + this->invert = inverted; + this->write(DOUT_HIZ); +} + +board::DigitalOut::~DigitalOut(void) +{ + this->write(DOUT_HIZ); +} + +void board::DigitalOut::write(int8_t level) +{ + if(level > 0) + { + this->last_set = DOUT_HIGH; + if(this->invert) mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); + else mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); + } + else if(level == 0) + { + this->last_set = DOUT_LOW; + if(this->invert) mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); + else mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); + } + else + { + this->last_set = DOUT_HIZ; + mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIZ); + } +} + +int8_t board::DigitalOut::get_set_level(void) +{ + return this->last_set; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/dout.h b/firmware/src/board/dout.h new file mode 100644 index 0000000..0cf0833 --- /dev/null +++ b/firmware/src/board/dout.h @@ -0,0 +1,36 @@ +#ifndef DIGITAL_OUTPUT_H_ +#define DIGITAL_OUTPUT_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +const int8_t DOUT_LOW = 0; +const int8_t DOUT_HIGH = 1; +const int8_t DOUT_HIZ = -1; + +class DigitalOut +{ + protected: + uint8_t gpio_ch; + uint8_t invert; + int8_t last_set; + + public: + DigitalOut(uint8_t gpio_ch, uint8_t inverted); + ~DigitalOut(void); + + void write(int8_t level); + int8_t get_set_level(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DIGITAL_OUTPUT_H_ */ \ No newline at end of file -- 2.49.1 From 6d5c8d226f2e3749f024088a5b4991d6602d63e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:45:17 +0300 Subject: [PATCH 05/35] Created OD common pwm class --- firmware/src/board/pwm.cpp | 40 ++++++++++++++++++++++++++++++++++++++ firmware/src/board/pwm.h | 31 +++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 firmware/src/board/pwm.cpp create mode 100644 firmware/src/board/pwm.h diff --git a/firmware/src/board/pwm.cpp b/firmware/src/board/pwm.cpp new file mode 100644 index 0000000..a557bac --- /dev/null +++ b/firmware/src/board/pwm.cpp @@ -0,0 +1,40 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "pwm.h" + +using namespace board; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +board::PWMout::PWMout(uint8_t pwm_ch) +{ + this->pwm_ch = pwm_ch; + this->write(0); +} + +board::PWMout::~PWMout(void) +{ + this->write(0); +} + +void board::PWMout::write(uint8_t duty) +{ + // Convert percent to 16b duty cycle + uint16_t dc = util::percent_to_16b(duty); + + // Set PWM + mcu::pwm_write(this->pwm_ch, dc); + this->last_duty = duty; +} + +uint8_t board::PWMout::get_set_duty(void) +{ + return this->last_duty; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/board/pwm.h b/firmware/src/board/pwm.h new file mode 100644 index 0000000..af96a1b --- /dev/null +++ b/firmware/src/board/pwm.h @@ -0,0 +1,31 @@ +#ifndef PWM_H_ +#define PWM_H_ + +/**** Includes ****/ +#include + +namespace board { + +/**** Public definitions ****/ +class PWMout +{ + protected: + uint8_t pwm_ch; + uint8_t last_duty; + + public: + PWMout(uint8_t pwm_ch); + ~PWMout(void); + + void write(uint8_t duty); + uint8_t get_set_duty(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* PWM_H_ */ \ No newline at end of file -- 2.49.1 From 6199f3c43fe8b51031b9d76621819cd9cd6b1223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:45:32 +0300 Subject: [PATCH 06/35] Created default mapping --- firmware/src/board/udccd_board.h | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 firmware/src/board/udccd_board.h diff --git a/firmware/src/board/udccd_board.h b/firmware/src/board/udccd_board.h new file mode 100644 index 0000000..5de104c --- /dev/null +++ b/firmware/src/board/udccd_board.h @@ -0,0 +1,44 @@ +#ifndef UDCCD_BOARD_H_ +#define UDCCD_BOARD_H_ + +/**** Includes ****/ +#include + +using namespace board; + +/**** Public definitions ****/ +static AnalogIn dccd_i(mcu::ADC0); +static AnalogIn dccd_u(mcu::ADC1); +static AnalogIn bat_u(mcu::ADC2); +static AnalogIn bat_i(mcu::ADC3); + +static Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); + +static AnalogIn ain1(mcu::ADC5); +static AnalogIn ain2(mcu::ADC4); + +static DigitalIn din1(mcu::GPIO0, 0, board::DIN_HIGH); +static DigitalIn din2(mcu::GPIO1, 0, board::DIN_HIGH); +static DigitalIn din3(mcu::GPIO2, 0, board::DIN_HIGH); +static DigitalIn din4(mcu::GPIO3, 0, board::DIN_HIGH); + +static DigitalIn hvdin1(mcu::GPIO4, 1, board::DIN_LOW); +static DigitalIn hvdin2(mcu::GPIO5, 1, board::DIN_LOW); +static DigitalIn hvdin3(mcu::GPIO6, 1, board::DIN_LOW); +static DigitalIO hvdin3_pull(mcu::GPIO3, board::DIN_HIGH); + +static DigitalOut odout1(mcu::GPIO9, 1); +static DigitalOut odout2(mcu::GPIO10, 1); +static DigitalOut odout3(mcu::GPIO11, 1); +static DigitalOut odout4(mcu::GPIO12, 1); +static DigitalOut odout5(mcu::GPIO13, 1); +static DigitalOut odout6(mcu::GPIO14, 1); + +static PWMout od_pwm(mcu::PWM1); + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +#endif /* UDCCD_BOARD_H_ */ \ No newline at end of file -- 2.49.1 From dda6c7a2ad7819129e99ca8c1a7ccc301c41a8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:45:50 +0300 Subject: [PATCH 07/35] Created button class --- firmware/src/hw/button.cpp | 85 ++++++++++++++++++++++++++++++++++++++ firmware/src/hw/button.h | 41 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 firmware/src/hw/button.cpp create mode 100644 firmware/src/hw/button.h diff --git a/firmware/src/hw/button.cpp b/firmware/src/hw/button.cpp new file mode 100644 index 0000000..12fc029 --- /dev/null +++ b/firmware/src/hw/button.cpp @@ -0,0 +1,85 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "button.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::Button::Button(board::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state) +{ + this->din_ch = din_ch; + + if(act_lvl) this->act_lvl = board::DIN_HIGH; + else this->act_lvl = board::DIN_LOW; + + this->dbnc_cnter = 0; + this->dbnc_lim = dbnc_lim; + + if(init_state) this->state = BUTTON_ON; + else this->state = BUTTON_OFF; + + this->time = 0; + this->is_new = 0; +} + +hw::Button::~Button(void) +{ + return; +} + +uint8_t hw::Button::read(void) +{ + // Read din level + uint8_t lvl = this->din_ch->read(); + + // Increase state counter + this->time = util::sat_add(this->time, 1); + + // Determine next state + uint8_t next_state = BUTTON_OFF; + if(lvl==this->act_lvl) next_state = BUTTON_ON; + + // Advance debounce sample counter + if(next_state != this->state) this->dbnc_cnter++; + else this->dbnc_cnter = 0; + + // Check for debounce end + if(this->dbnc_cnter < this->dbnc_lim) return this->state; + + // Debounce end. Apply new state. + this->state = next_state; + this->time = 0; + this->is_new = 1; + this->dbnc_cnter = 0; + + return this->state; +} + +uint8_t hw::Button::force_read(void) +{ + // Read din level + uint8_t lvl = this->din_ch->read(); + + // Cancels active debounce + this->dbnc_cnter = 0; + + // Determine next state + uint8_t next_state = BUTTON_OFF; + if(lvl==this->act_lvl) next_state = BUTTON_ON; + + if(next_state != this->state) + { + this->state = next_state; + this->time = 0; + this->is_new = 1; + }; + + return this->state; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/button.h b/firmware/src/hw/button.h new file mode 100644 index 0000000..8c7b87e --- /dev/null +++ b/firmware/src/hw/button.h @@ -0,0 +1,41 @@ +#ifndef BUTTON_H_ +#define BUTTON_H_ + +/**** Includes ****/ +#include +#include "../board/din.h" + +namespace hw { + +/**** Public definitions ****/ +const uint8_t BUTTON_OFF = 0; +const uint8_t BUTTON_ON = 1; + +class Button +{ + protected: + board::DigitalIn* din_ch; + uint8_t act_lvl; + uint8_t dbnc_cnter; + + public: + Button(board::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state); + ~Button(void); + + uint8_t state; + uint16_t time; + uint8_t dbnc_lim; + uint8_t is_new; + + uint8_t read(void); + uint8_t force_read(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* BUTTON_H_ */ \ No newline at end of file -- 2.49.1 From 417ecf41282dad64f65dff973128c91a78ae4b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:46:00 +0300 Subject: [PATCH 08/35] Created LED display class --- firmware/src/hw/display_led.cpp | 180 ++++++++++++++++++++++++++++++++ firmware/src/hw/display_led.h | 47 +++++++++ 2 files changed, 227 insertions(+) create mode 100644 firmware/src/hw/display_led.cpp create mode 100644 firmware/src/hw/display_led.h diff --git a/firmware/src/hw/display_led.cpp b/firmware/src/hw/display_led.cpp new file mode 100644 index 0000000..8d70ac8 --- /dev/null +++ b/firmware/src/hw/display_led.cpp @@ -0,0 +1,180 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "display_led.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +static uint8_t img_gen_dot10(uint8_t percent); +static uint8_t img_gen_dot20(uint8_t percent); +static uint8_t img_gen_bar(uint8_t percent); + +/**** Public function definitions ****/ +hw::DisplayLed::DisplayLed(board::DigitalOut* led0, board::DigitalOut* led1, board::DigitalOut* led2, board::DigitalOut* led3, board::DigitalOut* led4, board::DigitalOut* led5, board::PWMout* common) +{ + this->led0 = led0; + this->led1 = led1; + this->led2 = led2; + this->led3 = led3; + this->led4 = led4; + this->led5 = led5; + this->common = common; + + this->led0->write(0); + this->led1->write(0); + this->led2->write(0); + this->led3->write(0); + this->led4->write(0); + this->led5->write(0); + this->common->write(0); +} + +hw::DisplayLed::~DisplayLed(void) +{ + this->led0->write(0); + this->led1->write(0); + this->led2->write(0); + this->led3->write(0); + this->led4->write(0); + this->led5->write(0); + this->common->write(0); +} + +void hw::DisplayLed::show_percent(uint8_t percent, style_t style) +{ + uint8_t image = 0x00; + + switch(style) + { + case LED_DSP_BAR: + image = img_gen_bar(percent); + break; + + case LED_DSP_DOT10: + image = img_gen_dot10(percent); + break; + + default: + image = img_gen_dot20(percent); + break; + } + + this->write(image); +} + +void hw::DisplayLed::write(uint8_t image) +{ + if(image&0x01) this->led0->write(1); + else this->led0->write(0); + + if(image&0x02) this->led1->write(1); + else this->led1->write(0); + + if(image&0x04) this->led2->write(1); + else this->led2->write(0); + + if(image&0x08) this->led3->write(1); + else this->led3->write(0); + + if(image&0x10) this->led4->write(1); + else this->led4->write(0); + + if(image&0x20) this->led5->write(1); + else this->led5->write(0); +} + +void hw::DisplayLed::set_brigthness(uint8_t percent) +{ + this->common->write(percent); +} + +/**** Private function definitions ****/ +static uint8_t img_gen_dot10(uint8_t percent) +{ + switch(percent) + { + case 0 ... 5: + return 0x01; + + case 6 ... 15: + return 0x03; + + case 16 ... 25: + return 0x02; + + case 26 ... 35: + return 0x06; + + case 36 ... 45: + return 0x04; + + case 46 ... 55: + return 0x0C; + + case 56 ... 65: + return 0x08; + + case 66 ... 75: + return 0x18; + + case 76 ... 85: + return 0x10; + + case 86 ... 95: + return 0x30; + + default: + return 0x20; + } +} + +static uint8_t img_gen_dot20(uint8_t percent) +{ + switch(percent) + { + case 0 ... 10: + return 0x01; + + case 11 ... 30: + return 0x02; + + case 31 ... 50: + return 0x04; + + case 51 ... 70: + return 0x08; + + case 71 ... 90: + return 0x10; + + default: + return 0x20; + } +} + +static uint8_t img_gen_bar(uint8_t percent) +{ + switch(percent) + { + case 0 ... 10: + return 0x01; + + case 11 ... 30: + return 0x03; + + case 31 ... 50: + return 0x07; + + case 51 ... 70: + return 0x0F; + + case 71 ... 90: + return 0x1F; + + default: + return 0x3F; + } +} \ No newline at end of file diff --git a/firmware/src/hw/display_led.h b/firmware/src/hw/display_led.h new file mode 100644 index 0000000..8bba1aa --- /dev/null +++ b/firmware/src/hw/display_led.h @@ -0,0 +1,47 @@ +#ifndef DISPLAY_LED_H_ +#define DISPLAY_LED_H_ + +/**** Includes ****/ +#include +#include "../board/dout.h" +#include "../board/pwm.h" + +namespace hw { + +/**** Public definitions ****/ + +class DisplayLed +{ + protected: + board::DigitalOut* led0; + board::DigitalOut* led1; + board::DigitalOut* led2; + board::DigitalOut* led3; + board::DigitalOut* led4; + board::DigitalOut* led5; + board::PWMout* common; + + public: + typedef enum { + LED_DSP_DOT20, + LED_DSP_DOT10, + LED_DSP_BAR + } style_t; + + DisplayLed(board::DigitalOut* led0, board::DigitalOut* led1, board::DigitalOut* led2, board::DigitalOut* led3, board::DigitalOut* led4, board::DigitalOut* led5, board::PWMout* common); + ~DisplayLed(void); + + void show_percent(uint8_t percent, style_t style); + void write(uint8_t image); + + void set_brigthness(uint8_t percent); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DISPLAY_LED_H_ */ \ No newline at end of file -- 2.49.1 From 78de20e05b19abe70c54adbbbf811103543020d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:46:07 +0300 Subject: [PATCH 09/35] Created pot class --- firmware/src/hw/potentiometer.cpp | 38 +++++++++++++++++++++++++++++++ firmware/src/hw/potentiometer.h | 35 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 firmware/src/hw/potentiometer.cpp create mode 100644 firmware/src/hw/potentiometer.h diff --git a/firmware/src/hw/potentiometer.cpp b/firmware/src/hw/potentiometer.cpp new file mode 100644 index 0000000..0c88a36 --- /dev/null +++ b/firmware/src/hw/potentiometer.cpp @@ -0,0 +1,38 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "../utils/interpolate.h" +#include "potentiometer.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::Potentiometer::Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) +{ + this->ain_ch = ain_ch; + this->low_deadzone = low_deadzone; + this->high_deadzone = high_deadzone; + this->percent = 0; +} + +hw::Potentiometer::~Potentiometer(void) +{ + return; +} + +uint8_t hw::Potentiometer::read(void) +{ + // Update input + this->ain_ch->read(); + + // Calculate percent + this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); + + return this->percent; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/potentiometer.h b/firmware/src/hw/potentiometer.h new file mode 100644 index 0000000..5a853cc --- /dev/null +++ b/firmware/src/hw/potentiometer.h @@ -0,0 +1,35 @@ +#ifndef POTENTIOMETER_H_ +#define POTENTIOMETER_H_ + +/**** Includes ****/ +#include +#include "../board/ain.h" + +namespace hw { + +/**** Public definitions ****/ + +class Potentiometer +{ + protected: + board::AnalogIn* ain_ch; + + public: + Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone); + ~Potentiometer(void); + + uint16_t low_deadzone; + uint16_t high_deadzone; + uint8_t percent; + + uint8_t read(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* POTENTIOMETER_H_ */ \ No newline at end of file -- 2.49.1 From 8f7e5036e72725b92915af09a367f808120f2ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:46:17 +0300 Subject: [PATCH 10/35] Started coil driver class --- firmware/src/hw/cc_driver.cpp | 38 +++++++++++++++++++++++++++++++++++ firmware/src/hw/cc_driver.h | 33 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 firmware/src/hw/cc_driver.cpp create mode 100644 firmware/src/hw/cc_driver.h diff --git a/firmware/src/hw/cc_driver.cpp b/firmware/src/hw/cc_driver.cpp new file mode 100644 index 0000000..0c88a36 --- /dev/null +++ b/firmware/src/hw/cc_driver.cpp @@ -0,0 +1,38 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "../utils/interpolate.h" +#include "potentiometer.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::Potentiometer::Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) +{ + this->ain_ch = ain_ch; + this->low_deadzone = low_deadzone; + this->high_deadzone = high_deadzone; + this->percent = 0; +} + +hw::Potentiometer::~Potentiometer(void) +{ + return; +} + +uint8_t hw::Potentiometer::read(void) +{ + // Update input + this->ain_ch->read(); + + // Calculate percent + this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); + + return this->percent; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/cc_driver.h b/firmware/src/hw/cc_driver.h new file mode 100644 index 0000000..834c8c2 --- /dev/null +++ b/firmware/src/hw/cc_driver.h @@ -0,0 +1,33 @@ +#ifndef CONST_CURRENT_DRIVER_H_ +#define CONST_CURRENT_DRIVER_H_ + +/**** Includes ****/ +#include +#include "../board/ain.h" +#include "../board/halfbridge.h" + +namespace hw { + +/**** Public definitions ****/ + +class CCdriver +{ + protected: + board::AnalogIn* sup_voltage; + board::AnalogIn* sup_current; + board::AnalogIn* ain_ch; + board::AnalogIn* ain_ch; + + public: + CCdriver(board::AnalogIn* ain_ch); + ~CCdriver(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* CONST_CURRENT_DRIVER_H_ */ \ No newline at end of file -- 2.49.1 From f320bfefb79c42c83330ee887d9162ce5c2880e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 15:46:26 +0300 Subject: [PATCH 11/35] Changes --- firmware/src/main.cpp | 66 +++++++------------------------------- firmware/src/uDCCD.cppproj | 48 +++++++++++++++++++++------ 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 16d5bba..a2517e0 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,33 +1,23 @@ /**** Includes ****/ #include "utils/utils.h" + #include "board/mcu/mcu_hal.h" #include "board/ain.h" +#include "board/din.h" +#include "board/dout.h" #include "board/dio.h" -#include "board/hvdin.h" #include "board/halfbridge.h" -#include "board/od_com.h" -#include "board/odout.h" +#include "board/pwm.h" + +#include "hw/button.h" +#include "hw/potentiometer.h" +#include "hw/display_led.h" + +#include "board/udccd_board.h" /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ -static board::AnalogIn dccd_i(mcu::ADC0); -static board::AnalogIn dccd_u(mcu::ADC1); -static board::AnalogIn bat_u(mcu::ADC2); -static board::AnalogIn bat_i(mcu::ADC3); - -static board::AnalogIn ain1(mcu::ADC5); -static board::AnalogIn ain2(mcu::ADC4); - -static board::DigitalIO din1(mcu::GPIO0, board::DIO_HIGH); -static board::DigitalIO din2(mcu::GPIO1, board::DIO_HIGH); -static board::DigitalIO din3(mcu::GPIO2, board::DIO_HIGH); -static board::DigitalIO din4(mcu::GPIO3, board::DIO_HIGH); - -static board::HVDigitalIn hvdin1(mcu::GPIO4, board::HVDIN_LOW); -static board::HVDigitalIn hvdin2(mcu::GPIO5, board::HVDIN_LOW); -static board::HVDigitalIn hvdin3(mcu::GPIO6, board::HVDIN_LOW); - /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) @@ -39,42 +29,10 @@ int main(void) mcu_cfg.pwm_ch1_en = 1; mcu::startup(&mcu_cfg); - - dccd_i.mul = 1; - dccd_i.div = 1; - dccd_i.offset = 0; - - dccd_u.mul = 1; - dccd_u.div = 1; - dccd_u.offset = 0; - - bat_u.mul = 1; - bat_u.div = 1; - bat_u.offset = 0; - - bat_i.mul = 1; - bat_i.div = 1; - bat_i.offset = 0; - + // Super loop while(1) - { - dccd_i.read(); - dccd_u.read(); - bat_u.read(); - bat_i.read(); - ain1.read(); - ain2.read(); - - din1.read(); - din2.read(); - din3.read(); - din4.read(); - - hvdin1.read(); - hvdin2.read(); - hvdin3.read(); - + { continue; // End of super loop } diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 327eb2a..1b21809 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -159,6 +159,12 @@ compile + + compile + + + compile + compile @@ -171,28 +177,49 @@ compile - - compile - - - compile - compile compile - + compile - + compile - + compile - + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + compile @@ -214,6 +241,7 @@ + -- 2.49.1 From 4adcb7eba9d64f8b191fa5bab8af74dfb8619325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 17:53:54 +0300 Subject: [PATCH 12/35] saved work --- firmware/src/hw/cc_driver.cpp | 38 --------------------------------- firmware/src/hw/cc_driver.h | 33 ----------------------------- firmware/src/hw/cv_driver.cpp | 40 +++++++++++++++++++++++++++++++++++ firmware/src/hw/cv_driver.h | 37 ++++++++++++++++++++++++++++++++ firmware/src/uDCCD.cppproj | 4 ++-- 5 files changed, 79 insertions(+), 73 deletions(-) delete mode 100644 firmware/src/hw/cc_driver.cpp delete mode 100644 firmware/src/hw/cc_driver.h create mode 100644 firmware/src/hw/cv_driver.cpp create mode 100644 firmware/src/hw/cv_driver.h diff --git a/firmware/src/hw/cc_driver.cpp b/firmware/src/hw/cc_driver.cpp deleted file mode 100644 index 0c88a36..0000000 --- a/firmware/src/hw/cc_driver.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "../utils/interpolate.h" -#include "potentiometer.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::Potentiometer::Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) -{ - this->ain_ch = ain_ch; - this->low_deadzone = low_deadzone; - this->high_deadzone = high_deadzone; - this->percent = 0; -} - -hw::Potentiometer::~Potentiometer(void) -{ - return; -} - -uint8_t hw::Potentiometer::read(void) -{ - // Update input - this->ain_ch->read(); - - // Calculate percent - this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); - - return this->percent; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/cc_driver.h b/firmware/src/hw/cc_driver.h deleted file mode 100644 index 834c8c2..0000000 --- a/firmware/src/hw/cc_driver.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CONST_CURRENT_DRIVER_H_ -#define CONST_CURRENT_DRIVER_H_ - -/**** Includes ****/ -#include -#include "../board/ain.h" -#include "../board/halfbridge.h" - -namespace hw { - -/**** Public definitions ****/ - -class CCdriver -{ - protected: - board::AnalogIn* sup_voltage; - board::AnalogIn* sup_current; - board::AnalogIn* ain_ch; - board::AnalogIn* ain_ch; - - public: - CCdriver(board::AnalogIn* ain_ch); - ~CCdriver(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* CONST_CURRENT_DRIVER_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cv_driver.cpp b/firmware/src/hw/cv_driver.cpp new file mode 100644 index 0000000..734d8eb --- /dev/null +++ b/firmware/src/hw/cv_driver.cpp @@ -0,0 +1,40 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "cv_driver.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::CVdriver::CVdriver(board::AnalogIn* sup_voltage, board::AnalogIn* out_voltage, board::Hafbridge* hbridge) +{ + this->sup_voltage = sup_voltage; + this->hbridge = hbridge; + this->target = 0; + this->off = 1; +} + +hw::CVdriver::~CVdriver(void) +{ + return; +} + +void hw::CVdriver::update(void) +{ + // Update all inputs + this->sup_voltage->read(); + + // Calculate ratio + uint16_t ratio = util::sat_ratio(this->target, this->sup_voltage->last_read); + + // Set output + this->hbridge->write(ratio); +} + + + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/cv_driver.h b/firmware/src/hw/cv_driver.h new file mode 100644 index 0000000..a39e357 --- /dev/null +++ b/firmware/src/hw/cv_driver.h @@ -0,0 +1,37 @@ +#ifndef CONST_VOLTAGE_DRIVER_H_ +#define CONST_VOLTAGE_DRIVER_H_ + +/**** Includes ****/ +#include +#include "../board/ain.h" +#include "../board/halfbridge.h" + +namespace hw { + +/**** Public definitions ****/ + +class CVdriver +{ + protected: + board::AnalogIn* sup_voltage; + + board::Hafbridge* hbridge; + + public: + CVdriver(board::AnalogIn* sup_voltage, board::Hafbridge* hbridge); + ~CVdriver(void); + + uint16_t target = 0; + uint8_t off = 0; + + void process(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* CONST_VOLTAGE_DRIVER_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 1b21809..7601bb2 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -204,10 +204,10 @@ compile - + compile - + compile -- 2.49.1 From 8bac1f47872323213c5473fea4bf50503e2faa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 21:04:31 +0300 Subject: [PATCH 13/35] Finished const voltage output --- firmware/src/board/halfbridge.cpp | 22 ++++++++----- firmware/src/board/halfbridge.h | 7 +++-- firmware/src/hw/cv_driver.cpp | 40 ------------------------ firmware/src/hw/cv_driver.h | 37 ---------------------- firmware/src/hw/cv_otput.cpp | 52 +++++++++++++++++++++++++++++++ firmware/src/hw/cv_output.h | 38 ++++++++++++++++++++++ firmware/src/uDCCD.cppproj | 4 +-- 7 files changed, 110 insertions(+), 90 deletions(-) delete mode 100644 firmware/src/hw/cv_driver.cpp delete mode 100644 firmware/src/hw/cv_driver.h create mode 100644 firmware/src/hw/cv_otput.cpp create mode 100644 firmware/src/hw/cv_output.h diff --git a/firmware/src/board/halfbridge.cpp b/firmware/src/board/halfbridge.cpp index 6ede670..7047e32 100644 --- a/firmware/src/board/halfbridge.cpp +++ b/firmware/src/board/halfbridge.cpp @@ -16,8 +16,10 @@ board::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_d this->pwm_ch = hs_pwm_ch; this->gpio_ch = ls_gpio_ch; - if(max_dc>100) this->max_dc = 100; - else this->max_dc = max_dc; + if(max_dc>100) max_dc = 100; + + this->max_dc = util::percent_to_16b(max_dc); + this->disable(); } @@ -27,18 +29,22 @@ board::Hafbridge::~Hafbridge(void) this->disable(); } -void board::Hafbridge::write(uint8_t duty) +void board::Hafbridge::write(uint16_t dividend) { // Limit duty - if(duty > this->max_dc) duty = this->max_dc; - this->last_duty = duty; + if(dividend > this->max_dc) dividend = this->max_dc; + this->last_duty = dividend; if(this->enabled == 0) return; - // Convert percent to 16b duty cycle - uint16_t dc = util::percent_to_16b(duty); // Set PWM - mcu::pwm_write(this->pwm_ch, dc); + mcu::pwm_write(this->pwm_ch, dividend); +} + +void board::Hafbridge::write(uint8_t percent) +{ + // Convert to dividend/0xFFFF + this->write(util::percent_to_16b(percent)); } void board::Hafbridge::enable(void) diff --git a/firmware/src/board/halfbridge.h b/firmware/src/board/halfbridge.h index ef6414f..6ea7a63 100644 --- a/firmware/src/board/halfbridge.h +++ b/firmware/src/board/halfbridge.h @@ -12,15 +12,16 @@ class Hafbridge protected: uint8_t pwm_ch; uint8_t gpio_ch; - uint8_t last_duty; + uint16_t last_duty; uint8_t enabled; - uint8_t max_dc; + uint16_t max_dc; public: Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc); ~Hafbridge(void); - void write(uint8_t duty); + void write(uint16_t dividend); + void write(uint8_t percent); void enable(void); void disable(void); uint8_t get_set_duty(void); diff --git a/firmware/src/hw/cv_driver.cpp b/firmware/src/hw/cv_driver.cpp deleted file mode 100644 index 734d8eb..0000000 --- a/firmware/src/hw/cv_driver.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "cv_driver.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::CVdriver::CVdriver(board::AnalogIn* sup_voltage, board::AnalogIn* out_voltage, board::Hafbridge* hbridge) -{ - this->sup_voltage = sup_voltage; - this->hbridge = hbridge; - this->target = 0; - this->off = 1; -} - -hw::CVdriver::~CVdriver(void) -{ - return; -} - -void hw::CVdriver::update(void) -{ - // Update all inputs - this->sup_voltage->read(); - - // Calculate ratio - uint16_t ratio = util::sat_ratio(this->target, this->sup_voltage->last_read); - - // Set output - this->hbridge->write(ratio); -} - - - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/cv_driver.h b/firmware/src/hw/cv_driver.h deleted file mode 100644 index a39e357..0000000 --- a/firmware/src/hw/cv_driver.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CONST_VOLTAGE_DRIVER_H_ -#define CONST_VOLTAGE_DRIVER_H_ - -/**** Includes ****/ -#include -#include "../board/ain.h" -#include "../board/halfbridge.h" - -namespace hw { - -/**** Public definitions ****/ - -class CVdriver -{ - protected: - board::AnalogIn* sup_voltage; - - board::Hafbridge* hbridge; - - public: - CVdriver(board::AnalogIn* sup_voltage, board::Hafbridge* hbridge); - ~CVdriver(void); - - uint16_t target = 0; - uint8_t off = 0; - - void process(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* CONST_VOLTAGE_DRIVER_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cv_otput.cpp b/firmware/src/hw/cv_otput.cpp new file mode 100644 index 0000000..f2c25f8 --- /dev/null +++ b/firmware/src/hw/cv_otput.cpp @@ -0,0 +1,52 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "cv_output.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::CVoutput::CVoutput(board::Hafbridge* hbridge) +{ + this->hbridge = hbridge; + this->target = 0; + this->min_out = 0; + this->hbridge->disable(); +} + +hw::CVoutput::~CVoutput(void) +{ + this->hbridge->write((uint16_t)0); + this->hbridge->disable(); + return; +} + +void hw::CVoutput::update(uint16_t supply_mv) +{ + // Check target + if((this->target < this->min_out)&&(this->target > 0)) this->target = this->min_out; + + // Set output + this->hbridge->write(util::sat_ratio(this->target, supply_mv)); +} + +void hw::CVoutput::enable(void) +{ + this->hbridge->enable(); +} + +void hw::CVoutput::disable(void) +{ + this->hbridge->disable(); +} + +uint8_t hw::CVoutput::is_enabled(void) +{ + return this->hbridge->is_enabled(); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/cv_output.h b/firmware/src/hw/cv_output.h new file mode 100644 index 0000000..75a0a27 --- /dev/null +++ b/firmware/src/hw/cv_output.h @@ -0,0 +1,38 @@ +#ifndef CONST_VOLTAGE_OUTPUT_H_ +#define CONST_VOLTAGE_OUTPUT_H_ + +/**** Includes ****/ +#include +#include "../board/ain.h" +#include "../board/halfbridge.h" + +namespace hw { + +/**** Public definitions ****/ + +class CVoutput +{ + protected: + board::Hafbridge* hbridge; + + public: + CVoutput(board::Hafbridge* hbridge); + ~CVoutput(void); + + uint16_t target; + uint16_t min_out; + + void update(uint16_t supply_mv); + void enable(void); + void disable(void); + uint8_t is_enabled(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 7601bb2..5fc501d 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -204,10 +204,10 @@ compile - + compile - + compile -- 2.49.1 From a05c53401f7124eb646d53ba53e345825343aa28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 10 Apr 2024 22:44:02 +0300 Subject: [PATCH 14/35] Saved work --- firmware/src/board/mcu/mcu_hal.h | 27 ---------- firmware/src/board/udccd_board.h | 44 --------------- firmware/src/hw/cv_output.h | 1 - firmware/src/main.cpp | 92 ++++++++++++++++++++++++++++---- firmware/src/uDCCD.cppproj | 3 -- 5 files changed, 83 insertions(+), 84 deletions(-) delete mode 100644 firmware/src/board/udccd_board.h diff --git a/firmware/src/board/mcu/mcu_hal.h b/firmware/src/board/mcu/mcu_hal.h index 42a6495..83c664c 100644 --- a/firmware/src/board/mcu/mcu_hal.h +++ b/firmware/src/board/mcu/mcu_hal.h @@ -8,33 +8,6 @@ namespace mcu { /**** Public definitions ****/ /* -GPIO0 Down -GPIO1 Up -GPIO2 Mode -GPIO3 Handbrake -GPIO4 Brakes -GPIO5 Dimm -GPIO6 LED0 -GPIO7 LED1 -GPIO8 LED2 -GPIO9 LED3 -GPIO10 LED4 -GPIO11 LED5 -GPIO12 DCCD Enable -GPIO13 Handbrake pull -GPIO14 Speed pull -GPIO15 DCCD PWM -GPIO16 LED PWM - -ADC0 Output current -ADC1 Output voltage -ADC2 Battery current -ADC3 Battery voltage -ADC4 Potentiometer -ADC5 Mode -ADC8 MCU temperature -ADC14 MCU internal reference -ADC15 MCU ground */ const uint8_t GPIO0 = 0; //PC5 Mode diff --git a/firmware/src/board/udccd_board.h b/firmware/src/board/udccd_board.h deleted file mode 100644 index 5de104c..0000000 --- a/firmware/src/board/udccd_board.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef UDCCD_BOARD_H_ -#define UDCCD_BOARD_H_ - -/**** Includes ****/ -#include - -using namespace board; - -/**** Public definitions ****/ -static AnalogIn dccd_i(mcu::ADC0); -static AnalogIn dccd_u(mcu::ADC1); -static AnalogIn bat_u(mcu::ADC2); -static AnalogIn bat_i(mcu::ADC3); - -static Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); - -static AnalogIn ain1(mcu::ADC5); -static AnalogIn ain2(mcu::ADC4); - -static DigitalIn din1(mcu::GPIO0, 0, board::DIN_HIGH); -static DigitalIn din2(mcu::GPIO1, 0, board::DIN_HIGH); -static DigitalIn din3(mcu::GPIO2, 0, board::DIN_HIGH); -static DigitalIn din4(mcu::GPIO3, 0, board::DIN_HIGH); - -static DigitalIn hvdin1(mcu::GPIO4, 1, board::DIN_LOW); -static DigitalIn hvdin2(mcu::GPIO5, 1, board::DIN_LOW); -static DigitalIn hvdin3(mcu::GPIO6, 1, board::DIN_LOW); -static DigitalIO hvdin3_pull(mcu::GPIO3, board::DIN_HIGH); - -static DigitalOut odout1(mcu::GPIO9, 1); -static DigitalOut odout2(mcu::GPIO10, 1); -static DigitalOut odout3(mcu::GPIO11, 1); -static DigitalOut odout4(mcu::GPIO12, 1); -static DigitalOut odout5(mcu::GPIO13, 1); -static DigitalOut odout6(mcu::GPIO14, 1); - -static PWMout od_pwm(mcu::PWM1); - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -#endif /* UDCCD_BOARD_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cv_output.h b/firmware/src/hw/cv_output.h index 75a0a27..37a5b66 100644 --- a/firmware/src/hw/cv_output.h +++ b/firmware/src/hw/cv_output.h @@ -3,7 +3,6 @@ /**** Includes ****/ #include -#include "../board/ain.h" #include "../board/halfbridge.h" namespace hw { diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index a2517e0..6e43c7c 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -12,23 +12,65 @@ #include "hw/button.h" #include "hw/potentiometer.h" #include "hw/display_led.h" - -#include "board/udccd_board.h" +#include "hw/cv_output.h" /**** Private definitions ****/ +static board::AnalogIn dccd_i(mcu::ADC0); +static board::AnalogIn dccd_u(mcu::ADC1); +static board::AnalogIn bat_u(mcu::ADC2); +static board::AnalogIn bat_i(mcu::ADC3); + +static board::Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); + +static board::AnalogIn ain1(mcu::ADC5); // mode +static board::AnalogIn ain2(mcu::ADC4); // pot + +static board::DigitalIn din1(mcu::GPIO0, 0, board::DIN_HIGH); //mode +static board::DigitalIn din2(mcu::GPIO1, 0, board::DIN_HIGH); //pot +static board::DigitalIn din3(mcu::GPIO2, 0, board::DIN_HIGH); //down +static board::DigitalIn din4(mcu::GPIO3, 0, board::DIN_HIGH); //up + +static board::DigitalIn hvdin1(mcu::GPIO4, 1, board::DIN_LOW); //dimm +static board::DigitalIn hvdin2(mcu::GPIO5, 1, board::DIN_LOW); //brakes +static board::DigitalIn hvdin3(mcu::GPIO6, 1, board::DIN_LOW); //hbrake +static board::DigitalIO hvdin3_pull(mcu::GPIO7, board::DIN_HIGH); //hbrake pull + +static board::DigitalOut odout1(mcu::GPIO9, 1); +static board::DigitalOut odout2(mcu::GPIO10, 1); +static board::DigitalOut odout3(mcu::GPIO11, 1); +static board::DigitalOut odout4(mcu::GPIO12, 1); +static board::DigitalOut odout5(mcu::GPIO13, 1); +static board::DigitalOut odout6(mcu::GPIO14, 1); + +static board::PWMout od_pwm(mcu::PWM1); + +static hw::Button btn_mode(&din1, board::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_up(&din4, board::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_down(&din3, board::DIN_LOW, 10, hw::BUTTON_OFF); + +static hw::Button sw_dimm(&hvdin1, board::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_brakes(&hvdin2, board::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_hbrake(&hvdin3, board::DIN_LOW, 10, hw::BUTTON_OFF); + +static hw::Potentiometer pot(&ain2, 500, 4500); + +static hw::DisplayLed display(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); + +static hw::CVoutput cvout(&hbridge); + /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ +static void board_setup(void); + /**** Public function definitions ****/ int main(void) { - mcu::startupCfg_t mcu_cfg; - mcu_cfg.adc_clk = mcu::ADC_DIV2; - mcu_cfg.pwm_clk = mcu::TIM_DIV1; - mcu_cfg.pwm_top = 200; - mcu_cfg.pwm_ch1_en = 1; - - mcu::startup(&mcu_cfg); + board_setup(); + + cvout.target = 0; + cvout.min_out = 500; + cvout.enable(); // Super loop while(1) @@ -41,3 +83,35 @@ int main(void) } /**** Private function definitions ***/ +static void board_setup(void) +{ + mcu::startupCfg_t mcu_cfg; + mcu_cfg.adc_clk = mcu::ADC_DIV2; + mcu_cfg.pwm_clk = mcu::TIM_DIV1; + mcu_cfg.pwm_top = 200; + mcu_cfg.pwm_ch1_en = 1; + + mcu::startup(&mcu_cfg); + + dccd_i.mul = 215; + dccd_i.div = 22; + dccd_i.offset = 0; + dccd_i.last_read = 0; + + dccd_u.mul = 20; + dccd_u.div = 1; + dccd_u.offset = 0; + dccd_u.last_read = 0; + + bat_u.mul = 20; + bat_u.div = 1; + bat_u.offset = 0; + bat_u.last_read = 12000; + + bat_i.mul = 235; + bat_i.div = 6; + bat_i.offset = 0; + bat_i.last_read = 0; + + od_pwm.write(100); +} \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 5fc501d..5f63a83 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -195,9 +195,6 @@ compile - - compile - compile -- 2.49.1 From f1edc6e15ae97c5ab6d92953bbeaeb0d6f24e6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Thu, 11 Apr 2024 23:03:33 +0300 Subject: [PATCH 15/35] Changed logic --- firmware/src/{board => bsp}/ain.cpp | 6 +- firmware/src/{board => bsp}/ain.h | 2 +- firmware/src/{board => bsp}/din.cpp | 8 +- firmware/src/{board => bsp}/din.h | 2 +- firmware/src/{board => bsp}/dio.cpp | 8 +- firmware/src/{board => bsp}/dio.h | 2 +- firmware/src/{board => bsp}/dout.cpp | 10 +- firmware/src/{board => bsp}/dout.h | 2 +- firmware/src/{board => bsp}/halfbridge.cpp | 18 +-- firmware/src/{board => bsp}/halfbridge.h | 2 +- firmware/src/{board => bsp}/mcu/mcu_hal.h | 0 .../src/{board => bsp}/mcu/mcu_hal_r8.cpp | 0 firmware/src/{board => bsp}/pwm.cpp | 10 +- firmware/src/{board => bsp}/pwm.h | 2 +- firmware/src/hw/button.cpp | 12 +- firmware/src/hw/button.h | 10 +- firmware/src/hw/cv_otput.cpp | 9 +- firmware/src/hw/cv_output.h | 10 +- firmware/src/hw/display_led.cpp | 2 +- firmware/src/hw/display_led.h | 20 +-- firmware/src/hw/potentiometer.cpp | 7 +- firmware/src/hw/potentiometer.h | 8 +- firmware/src/main.cpp | 122 ++++++------------ firmware/src/uDCCD.cppproj | 32 ++--- 24 files changed, 129 insertions(+), 175 deletions(-) rename firmware/src/{board => bsp}/ain.cpp (86%) rename firmware/src/{board => bsp}/ain.h (96%) rename firmware/src/{board => bsp}/din.cpp (78%) rename firmware/src/{board => bsp}/din.h (96%) rename firmware/src/{board => bsp}/dio.cpp (68%) rename firmware/src/{board => bsp}/dio.h (96%) rename firmware/src/{board => bsp}/dout.cpp (80%) rename firmware/src/{board => bsp}/dout.h (96%) rename firmware/src/{board => bsp}/halfbridge.cpp (73%) rename firmware/src/{board => bsp}/halfbridge.h (97%) rename firmware/src/{board => bsp}/mcu/mcu_hal.h (100%) rename firmware/src/{board => bsp}/mcu/mcu_hal_r8.cpp (100%) rename firmware/src/{board => bsp}/pwm.cpp (76%) rename firmware/src/{board => bsp}/pwm.h (95%) diff --git a/firmware/src/board/ain.cpp b/firmware/src/bsp/ain.cpp similarity index 86% rename from firmware/src/board/ain.cpp rename to firmware/src/bsp/ain.cpp index ab87212..1d4cc06 100644 --- a/firmware/src/board/ain.cpp +++ b/firmware/src/bsp/ain.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "ain.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,7 +11,7 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::AnalogIn::AnalogIn(uint8_t adc_ch) +bsp::AnalogIn::AnalogIn(uint8_t adc_ch) { this->adc_ch = adc_ch; this->mul = DEF_AIN_MUL; @@ -20,7 +20,7 @@ board::AnalogIn::AnalogIn(uint8_t adc_ch) this->last_read = 0; } -uint16_t board::AnalogIn::read(void) +uint16_t bsp::AnalogIn::read(void) { //Read ADC uint16_t raw = mcu::adc_read(this->adc_ch); diff --git a/firmware/src/board/ain.h b/firmware/src/bsp/ain.h similarity index 96% rename from firmware/src/board/ain.h rename to firmware/src/bsp/ain.h index 55809da..00b90b5 100644 --- a/firmware/src/board/ain.h +++ b/firmware/src/bsp/ain.h @@ -4,7 +4,7 @@ /**** Includes ****/ #include -namespace board { +namespace bsp { /**** Public definitions ****/ static const uint8_t DEF_AIN_MUL = 215; diff --git a/firmware/src/board/din.cpp b/firmware/src/bsp/din.cpp similarity index 78% rename from firmware/src/board/din.cpp rename to firmware/src/bsp/din.cpp index 5a89808..4ea82d3 100644 --- a/firmware/src/board/din.cpp +++ b/firmware/src/bsp/din.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "din.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,7 +11,7 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::DigitalIn::DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value) +bsp::DigitalIn::DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value) { this->gpio_ch = gpio_ch; this->invert = inverted; @@ -20,12 +20,12 @@ board::DigitalIn::DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_valu else this->last_read = DIN_LOW; } -board::DigitalIn::~DigitalIn(void) +bsp::DigitalIn::~DigitalIn(void) { return; } -uint8_t board::DigitalIn::read(void) +uint8_t bsp::DigitalIn::read(void) { uint8_t lvl = mcu::gpio_read(this->gpio_ch); diff --git a/firmware/src/board/din.h b/firmware/src/bsp/din.h similarity index 96% rename from firmware/src/board/din.h rename to firmware/src/bsp/din.h index 4b0776e..8d8e924 100644 --- a/firmware/src/board/din.h +++ b/firmware/src/bsp/din.h @@ -4,7 +4,7 @@ /**** Includes ****/ #include -namespace board { +namespace bsp { /**** Public definitions ****/ const uint8_t DIN_LOW = 0; diff --git a/firmware/src/board/dio.cpp b/firmware/src/bsp/dio.cpp similarity index 68% rename from firmware/src/board/dio.cpp rename to firmware/src/bsp/dio.cpp index 9dc2934..ead2112 100644 --- a/firmware/src/board/dio.cpp +++ b/firmware/src/bsp/dio.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "dio.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,17 +11,17 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_value) : DigitalIn(gpio_ch, 0, init_value), DigitalOut(gpio_ch, 0) +bsp::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_value) : DigitalIn(gpio_ch, 0, init_value), DigitalOut(gpio_ch, 0) { return; } -board::DigitalIO::~DigitalIO(void) +bsp::DigitalIO::~DigitalIO(void) { this->write(DOUT_HIZ); } -uint8_t board::DigitalIO::is_io_match(void) +uint8_t bsp::DigitalIO::is_io_match(void) { if(this->last_set == DOUT_HIZ) return 1; diff --git a/firmware/src/board/dio.h b/firmware/src/bsp/dio.h similarity index 96% rename from firmware/src/board/dio.h rename to firmware/src/bsp/dio.h index bddd90b..0506654 100644 --- a/firmware/src/board/dio.h +++ b/firmware/src/bsp/dio.h @@ -6,7 +6,7 @@ #include "din.h" #include "dout.h" -namespace board { +namespace bsp { /**** Public definitions ****/ const int8_t DIO_LOW = 0; diff --git a/firmware/src/board/dout.cpp b/firmware/src/bsp/dout.cpp similarity index 80% rename from firmware/src/board/dout.cpp rename to firmware/src/bsp/dout.cpp index b2e6608..fdb880b 100644 --- a/firmware/src/board/dout.cpp +++ b/firmware/src/bsp/dout.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "dout.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,19 +11,19 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::DigitalOut::DigitalOut(uint8_t gpio_ch, uint8_t inverted) +bsp::DigitalOut::DigitalOut(uint8_t gpio_ch, uint8_t inverted) { this->gpio_ch = gpio_ch; this->invert = inverted; this->write(DOUT_HIZ); } -board::DigitalOut::~DigitalOut(void) +bsp::DigitalOut::~DigitalOut(void) { this->write(DOUT_HIZ); } -void board::DigitalOut::write(int8_t level) +void bsp::DigitalOut::write(int8_t level) { if(level > 0) { @@ -44,7 +44,7 @@ void board::DigitalOut::write(int8_t level) } } -int8_t board::DigitalOut::get_set_level(void) +int8_t bsp::DigitalOut::get_set_level(void) { return this->last_set; } diff --git a/firmware/src/board/dout.h b/firmware/src/bsp/dout.h similarity index 96% rename from firmware/src/board/dout.h rename to firmware/src/bsp/dout.h index 0cf0833..49c696e 100644 --- a/firmware/src/board/dout.h +++ b/firmware/src/bsp/dout.h @@ -4,7 +4,7 @@ /**** Includes ****/ #include -namespace board { +namespace bsp { /**** Public definitions ****/ const int8_t DOUT_LOW = 0; diff --git a/firmware/src/board/halfbridge.cpp b/firmware/src/bsp/halfbridge.cpp similarity index 73% rename from firmware/src/board/halfbridge.cpp rename to firmware/src/bsp/halfbridge.cpp index 7047e32..23321be 100644 --- a/firmware/src/board/halfbridge.cpp +++ b/firmware/src/bsp/halfbridge.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "halfbridge.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,7 +11,7 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc) +bsp::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc) { this->pwm_ch = hs_pwm_ch; this->gpio_ch = ls_gpio_ch; @@ -23,13 +23,13 @@ board::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_d this->disable(); } -board::Hafbridge::~Hafbridge(void) +bsp::Hafbridge::~Hafbridge(void) { this->last_duty = 0; this->disable(); } -void board::Hafbridge::write(uint16_t dividend) +void bsp::Hafbridge::write(uint16_t dividend) { // Limit duty if(dividend > this->max_dc) dividend = this->max_dc; @@ -41,32 +41,32 @@ void board::Hafbridge::write(uint16_t dividend) mcu::pwm_write(this->pwm_ch, dividend); } -void board::Hafbridge::write(uint8_t percent) +void bsp::Hafbridge::write(uint8_t percent) { // Convert to dividend/0xFFFF this->write(util::percent_to_16b(percent)); } -void board::Hafbridge::enable(void) +void bsp::Hafbridge::enable(void) { mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); this->enabled = 1; this->write(this->last_duty); } -void board::Hafbridge::disable(void) +void bsp::Hafbridge::disable(void) { mcu::pwm_write(this->pwm_ch, 0); mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); this->enabled = 0; } -uint8_t board::Hafbridge::get_set_duty(void) +uint8_t bsp::Hafbridge::get_set_duty(void) { return this->last_duty; } -uint8_t board::Hafbridge::is_enabled(void) +uint8_t bsp::Hafbridge::is_enabled(void) { return this->enabled; } diff --git a/firmware/src/board/halfbridge.h b/firmware/src/bsp/halfbridge.h similarity index 97% rename from firmware/src/board/halfbridge.h rename to firmware/src/bsp/halfbridge.h index 6ea7a63..c75a21e 100644 --- a/firmware/src/board/halfbridge.h +++ b/firmware/src/bsp/halfbridge.h @@ -4,7 +4,7 @@ /**** Includes ****/ #include -namespace board { +namespace bsp { /**** Public definitions ****/ class Hafbridge diff --git a/firmware/src/board/mcu/mcu_hal.h b/firmware/src/bsp/mcu/mcu_hal.h similarity index 100% rename from firmware/src/board/mcu/mcu_hal.h rename to firmware/src/bsp/mcu/mcu_hal.h diff --git a/firmware/src/board/mcu/mcu_hal_r8.cpp b/firmware/src/bsp/mcu/mcu_hal_r8.cpp similarity index 100% rename from firmware/src/board/mcu/mcu_hal_r8.cpp rename to firmware/src/bsp/mcu/mcu_hal_r8.cpp diff --git a/firmware/src/board/pwm.cpp b/firmware/src/bsp/pwm.cpp similarity index 76% rename from firmware/src/board/pwm.cpp rename to firmware/src/bsp/pwm.cpp index a557bac..32db2c5 100644 --- a/firmware/src/board/pwm.cpp +++ b/firmware/src/bsp/pwm.cpp @@ -3,7 +3,7 @@ #include "mcu/mcu_hal.h" #include "pwm.h" -using namespace board; +using namespace bsp; /**** Private definitions ****/ /**** Private constants ****/ @@ -11,18 +11,18 @@ using namespace board; /**** Private function declarations ****/ /**** Public function definitions ****/ -board::PWMout::PWMout(uint8_t pwm_ch) +bsp::PWMout::PWMout(uint8_t pwm_ch) { this->pwm_ch = pwm_ch; this->write(0); } -board::PWMout::~PWMout(void) +bsp::PWMout::~PWMout(void) { this->write(0); } -void board::PWMout::write(uint8_t duty) +void bsp::PWMout::write(uint8_t duty) { // Convert percent to 16b duty cycle uint16_t dc = util::percent_to_16b(duty); @@ -32,7 +32,7 @@ void board::PWMout::write(uint8_t duty) this->last_duty = duty; } -uint8_t board::PWMout::get_set_duty(void) +uint8_t bsp::PWMout::get_set_duty(void) { return this->last_duty; } diff --git a/firmware/src/board/pwm.h b/firmware/src/bsp/pwm.h similarity index 95% rename from firmware/src/board/pwm.h rename to firmware/src/bsp/pwm.h index af96a1b..c5b6cef 100644 --- a/firmware/src/board/pwm.h +++ b/firmware/src/bsp/pwm.h @@ -4,7 +4,7 @@ /**** Includes ****/ #include -namespace board { +namespace bsp { /**** Public definitions ****/ class PWMout diff --git a/firmware/src/hw/button.cpp b/firmware/src/hw/button.cpp index 12fc029..cf584b2 100644 --- a/firmware/src/hw/button.cpp +++ b/firmware/src/hw/button.cpp @@ -10,12 +10,12 @@ using namespace hw; /**** Private function declarations ****/ /**** Public function definitions ****/ -hw::Button::Button(board::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state) +hw::Button::Button(bsp::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state) { this->din_ch = din_ch; - if(act_lvl) this->act_lvl = board::DIN_HIGH; - else this->act_lvl = board::DIN_LOW; + if(act_lvl) this->act_lvl = bsp::DIN_HIGH; + else this->act_lvl = bsp::DIN_LOW; this->dbnc_cnter = 0; this->dbnc_lim = dbnc_lim; @@ -32,10 +32,10 @@ hw::Button::~Button(void) return; } -uint8_t hw::Button::read(void) +uint8_t hw::Button::update(void) { // Read din level - uint8_t lvl = this->din_ch->read(); + uint8_t lvl = this->din_ch->last_read; // Increase state counter this->time = util::sat_add(this->time, 1); @@ -60,7 +60,7 @@ uint8_t hw::Button::read(void) return this->state; } -uint8_t hw::Button::force_read(void) +uint8_t hw::Button::force_update(void) { // Read din level uint8_t lvl = this->din_ch->read(); diff --git a/firmware/src/hw/button.h b/firmware/src/hw/button.h index 8c7b87e..b77e549 100644 --- a/firmware/src/hw/button.h +++ b/firmware/src/hw/button.h @@ -3,7 +3,7 @@ /**** Includes ****/ #include -#include "../board/din.h" +#include "../bsp/din.h" namespace hw { @@ -14,12 +14,12 @@ const uint8_t BUTTON_ON = 1; class Button { protected: - board::DigitalIn* din_ch; + bsp::DigitalIn* din_ch; uint8_t act_lvl; uint8_t dbnc_cnter; public: - Button(board::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state); + Button(bsp::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state); ~Button(void); uint8_t state; @@ -27,8 +27,8 @@ class Button uint8_t dbnc_lim; uint8_t is_new; - uint8_t read(void); - uint8_t force_read(void); + uint8_t update(void); + uint8_t force_update(void); }; /**** Public function declarations ****/ diff --git a/firmware/src/hw/cv_otput.cpp b/firmware/src/hw/cv_otput.cpp index f2c25f8..8913c9a 100644 --- a/firmware/src/hw/cv_otput.cpp +++ b/firmware/src/hw/cv_otput.cpp @@ -10,9 +10,10 @@ using namespace hw; /**** Private function declarations ****/ /**** Public function definitions ****/ -hw::CVoutput::CVoutput(board::Hafbridge* hbridge) +hw::CVoutput::CVoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u) { this->hbridge = hbridge; + this->supply = supply_u; this->target = 0; this->min_out = 0; this->hbridge->disable(); @@ -20,18 +21,18 @@ hw::CVoutput::CVoutput(board::Hafbridge* hbridge) hw::CVoutput::~CVoutput(void) { - this->hbridge->write((uint16_t)0); + this->hbridge->write((uint16_t)0x0000); this->hbridge->disable(); return; } -void hw::CVoutput::update(uint16_t supply_mv) +void hw::CVoutput::update(void) { // Check target if((this->target < this->min_out)&&(this->target > 0)) this->target = this->min_out; // Set output - this->hbridge->write(util::sat_ratio(this->target, supply_mv)); + this->hbridge->write(util::sat_ratio(this->target, this->supply->last_read)); } void hw::CVoutput::enable(void) diff --git a/firmware/src/hw/cv_output.h b/firmware/src/hw/cv_output.h index 37a5b66..03de3a9 100644 --- a/firmware/src/hw/cv_output.h +++ b/firmware/src/hw/cv_output.h @@ -3,7 +3,8 @@ /**** Includes ****/ #include -#include "../board/halfbridge.h" +#include "../bsp/ain.h" +#include "../bsp/halfbridge.h" namespace hw { @@ -12,16 +13,17 @@ namespace hw { class CVoutput { protected: - board::Hafbridge* hbridge; + bsp::Hafbridge* hbridge; + bsp::AnalogIn* supply; public: - CVoutput(board::Hafbridge* hbridge); + CVoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u); ~CVoutput(void); uint16_t target; uint16_t min_out; - void update(uint16_t supply_mv); + void update(void); void enable(void); void disable(void); uint8_t is_enabled(void); diff --git a/firmware/src/hw/display_led.cpp b/firmware/src/hw/display_led.cpp index 8d70ac8..801d0da 100644 --- a/firmware/src/hw/display_led.cpp +++ b/firmware/src/hw/display_led.cpp @@ -13,7 +13,7 @@ static uint8_t img_gen_dot20(uint8_t percent); static uint8_t img_gen_bar(uint8_t percent); /**** Public function definitions ****/ -hw::DisplayLed::DisplayLed(board::DigitalOut* led0, board::DigitalOut* led1, board::DigitalOut* led2, board::DigitalOut* led3, board::DigitalOut* led4, board::DigitalOut* led5, board::PWMout* common) +hw::DisplayLed::DisplayLed(bsp::DigitalOut* led0, bsp::DigitalOut* led1, bsp::DigitalOut* led2, bsp::DigitalOut* led3, bsp::DigitalOut* led4, bsp::DigitalOut* led5, bsp::PWMout* common) { this->led0 = led0; this->led1 = led1; diff --git a/firmware/src/hw/display_led.h b/firmware/src/hw/display_led.h index 8bba1aa..455e5ad 100644 --- a/firmware/src/hw/display_led.h +++ b/firmware/src/hw/display_led.h @@ -3,8 +3,8 @@ /**** Includes ****/ #include -#include "../board/dout.h" -#include "../board/pwm.h" +#include "../bsp/dout.h" +#include "../bsp/pwm.h" namespace hw { @@ -13,13 +13,13 @@ namespace hw { class DisplayLed { protected: - board::DigitalOut* led0; - board::DigitalOut* led1; - board::DigitalOut* led2; - board::DigitalOut* led3; - board::DigitalOut* led4; - board::DigitalOut* led5; - board::PWMout* common; + bsp::DigitalOut* led0; + bsp::DigitalOut* led1; + bsp::DigitalOut* led2; + bsp::DigitalOut* led3; + bsp::DigitalOut* led4; + bsp::DigitalOut* led5; + bsp::PWMout* common; public: typedef enum { @@ -28,7 +28,7 @@ class DisplayLed LED_DSP_BAR } style_t; - DisplayLed(board::DigitalOut* led0, board::DigitalOut* led1, board::DigitalOut* led2, board::DigitalOut* led3, board::DigitalOut* led4, board::DigitalOut* led5, board::PWMout* common); + DisplayLed(bsp::DigitalOut* led0, bsp::DigitalOut* led1, bsp::DigitalOut* led2, bsp::DigitalOut* led3, bsp::DigitalOut* led4, bsp::DigitalOut* led5, bsp::PWMout* common); ~DisplayLed(void); void show_percent(uint8_t percent, style_t style); diff --git a/firmware/src/hw/potentiometer.cpp b/firmware/src/hw/potentiometer.cpp index 0c88a36..6559b2b 100644 --- a/firmware/src/hw/potentiometer.cpp +++ b/firmware/src/hw/potentiometer.cpp @@ -11,7 +11,7 @@ using namespace hw; /**** Private function declarations ****/ /**** Public function definitions ****/ -hw::Potentiometer::Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) +hw::Potentiometer::Potentiometer(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) { this->ain_ch = ain_ch; this->low_deadzone = low_deadzone; @@ -24,11 +24,8 @@ hw::Potentiometer::~Potentiometer(void) return; } -uint8_t hw::Potentiometer::read(void) +uint8_t hw::Potentiometer::update(void) { - // Update input - this->ain_ch->read(); - // Calculate percent this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); diff --git a/firmware/src/hw/potentiometer.h b/firmware/src/hw/potentiometer.h index 5a853cc..2321f7f 100644 --- a/firmware/src/hw/potentiometer.h +++ b/firmware/src/hw/potentiometer.h @@ -3,7 +3,7 @@ /**** Includes ****/ #include -#include "../board/ain.h" +#include "../bsp/ain.h" namespace hw { @@ -12,17 +12,17 @@ namespace hw { class Potentiometer { protected: - board::AnalogIn* ain_ch; + bsp::AnalogIn* ain_ch; public: - Potentiometer(board::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone); + Potentiometer(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone); ~Potentiometer(void); uint16_t low_deadzone; uint16_t high_deadzone; uint8_t percent; - uint8_t read(void); + uint8_t update(void); }; /**** Public function declarations ****/ diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 6e43c7c..b730192 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,13 +1,13 @@ /**** Includes ****/ #include "utils/utils.h" -#include "board/mcu/mcu_hal.h" -#include "board/ain.h" -#include "board/din.h" -#include "board/dout.h" -#include "board/dio.h" -#include "board/halfbridge.h" -#include "board/pwm.h" +#include "bsp/mcu/mcu_hal.h" +#include "bsp/ain.h" +#include "bsp/din.h" +#include "bsp/dout.h" +#include "bsp/dio.h" +#include "bsp/halfbridge.h" +#include "bsp/pwm.h" #include "hw/button.h" #include "hw/potentiometer.h" @@ -15,63 +15,49 @@ #include "hw/cv_output.h" /**** Private definitions ****/ -static board::AnalogIn dccd_i(mcu::ADC0); -static board::AnalogIn dccd_u(mcu::ADC1); -static board::AnalogIn bat_u(mcu::ADC2); -static board::AnalogIn bat_i(mcu::ADC3); +/**** Private constants ****/ +/**** Private variables ****/ +static bsp::AnalogIn dccd_i(mcu::ADC0); +static bsp::AnalogIn dccd_u(mcu::ADC1); +static bsp::AnalogIn bat_u(mcu::ADC2); +static bsp::AnalogIn bat_i(mcu::ADC3); +static bsp::Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); +static bsp::AnalogIn ain1(mcu::ADC5); // mode +static bsp::AnalogIn ain2(mcu::ADC4); // pot +static bsp::DigitalIn din1(mcu::GPIO0, 0, bsp::DIN_HIGH); //mode +static bsp::DigitalIn din2(mcu::GPIO1, 0, bsp::DIN_HIGH); //pot +static bsp::DigitalIn din3(mcu::GPIO2, 0, bsp::DIN_HIGH); //down +static bsp::DigitalIn din4(mcu::GPIO3, 0, bsp::DIN_HIGH); //up +static bsp::DigitalIn hvdin1(mcu::GPIO4, 1, bsp::DIN_LOW); //dimm +static bsp::DigitalIn hvdin2(mcu::GPIO5, 1, bsp::DIN_LOW); //brakes +static bsp::DigitalIn hvdin3(mcu::GPIO6, 1, bsp::DIN_LOW); //hbrake +static bsp::DigitalIO hvdin3_pull(mcu::GPIO7, bsp::DIN_HIGH); //hbrake pull +static bsp::DigitalOut odout1(mcu::GPIO9, 1); +static bsp::DigitalOut odout2(mcu::GPIO10, 1); +static bsp::DigitalOut odout3(mcu::GPIO11, 1); +static bsp::DigitalOut odout4(mcu::GPIO12, 1); +static bsp::DigitalOut odout5(mcu::GPIO13, 1); +static bsp::DigitalOut odout6(mcu::GPIO14, 1); +static bsp::PWMout od_pwm(mcu::PWM1); -static board::Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); +static hw::Button btn_mode(&din1, bsp::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_up(&din4, bsp::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_down(&din3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); -static board::AnalogIn ain1(mcu::ADC5); // mode -static board::AnalogIn ain2(mcu::ADC4); // pot - -static board::DigitalIn din1(mcu::GPIO0, 0, board::DIN_HIGH); //mode -static board::DigitalIn din2(mcu::GPIO1, 0, board::DIN_HIGH); //pot -static board::DigitalIn din3(mcu::GPIO2, 0, board::DIN_HIGH); //down -static board::DigitalIn din4(mcu::GPIO3, 0, board::DIN_HIGH); //up - -static board::DigitalIn hvdin1(mcu::GPIO4, 1, board::DIN_LOW); //dimm -static board::DigitalIn hvdin2(mcu::GPIO5, 1, board::DIN_LOW); //brakes -static board::DigitalIn hvdin3(mcu::GPIO6, 1, board::DIN_LOW); //hbrake -static board::DigitalIO hvdin3_pull(mcu::GPIO7, board::DIN_HIGH); //hbrake pull - -static board::DigitalOut odout1(mcu::GPIO9, 1); -static board::DigitalOut odout2(mcu::GPIO10, 1); -static board::DigitalOut odout3(mcu::GPIO11, 1); -static board::DigitalOut odout4(mcu::GPIO12, 1); -static board::DigitalOut odout5(mcu::GPIO13, 1); -static board::DigitalOut odout6(mcu::GPIO14, 1); - -static board::PWMout od_pwm(mcu::PWM1); - -static hw::Button btn_mode(&din1, board::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_up(&din4, board::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_down(&din3, board::DIN_LOW, 10, hw::BUTTON_OFF); - -static hw::Button sw_dimm(&hvdin1, board::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_brakes(&hvdin2, board::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_hbrake(&hvdin3, board::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button sw_dimm(&hvdin1, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_brakes(&hvdin2, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_hbrake(&hvdin3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); static hw::Potentiometer pot(&ain2, 500, 4500); static hw::DisplayLed display(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); -static hw::CVoutput cvout(&hbridge); +static hw::CVoutput cvout(&hbridge, &bat_u); -/**** Private constants ****/ -/**** Private variables ****/ /**** Private function declarations ****/ -static void board_setup(void); - /**** Public function definitions ****/ int main(void) { - board_setup(); - - cvout.target = 0; - cvout.min_out = 500; - cvout.enable(); - // Super loop while(1) { @@ -83,35 +69,3 @@ int main(void) } /**** Private function definitions ***/ -static void board_setup(void) -{ - mcu::startupCfg_t mcu_cfg; - mcu_cfg.adc_clk = mcu::ADC_DIV2; - mcu_cfg.pwm_clk = mcu::TIM_DIV1; - mcu_cfg.pwm_top = 200; - mcu_cfg.pwm_ch1_en = 1; - - mcu::startup(&mcu_cfg); - - dccd_i.mul = 215; - dccd_i.div = 22; - dccd_i.offset = 0; - dccd_i.last_read = 0; - - dccd_u.mul = 20; - dccd_u.div = 1; - dccd_u.offset = 0; - dccd_u.last_read = 0; - - bat_u.mul = 20; - bat_u.div = 1; - bat_u.offset = 0; - bat_u.last_read = 12000; - - bat_i.mul = 235; - bat_i.div = 6; - bat_i.offset = 0; - bat_i.last_read = 0; - - od_pwm.write(100); -} \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 5f63a83..e57255b 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -153,46 +153,46 @@ - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile - + compile @@ -236,8 +236,8 @@ - - + + -- 2.49.1 From 22ac8240a2a00005efb682af9ab76721c22e1480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 11:51:10 +0300 Subject: [PATCH 16/35] Added absolute subtract --- firmware/src/utils/utils.cpp | 20 +++++++++++++++++++- firmware/src/utils/utils.h | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/firmware/src/utils/utils.cpp b/firmware/src/utils/utils.cpp index fae8eda..cc6dbe5 100644 --- a/firmware/src/utils/utils.cpp +++ b/firmware/src/utils/utils.cpp @@ -57,7 +57,6 @@ uint16_t util::sat_subtract(uint16_t x, uint16_t y) if(z > x) return 0; else return z; } - uint32_t util::sat_subtract(uint32_t x, uint32_t y) { uint32_t z = x - y; @@ -66,6 +65,25 @@ uint32_t util::sat_subtract(uint32_t x, uint32_t y) else return z; } +uint8_t util::abs_subtract(uint8_t x, uint8_t y) +{ + if(x > y) return x - y; + else return y-x; +} + +uint16_t util::abs_subtract(uint16_t x, uint16_t y) +{ + if(x > y) return x - y; + else return y-x; +} + +uint32_t util::abs_subtract(uint32_t x, uint32_t y) +{ + if(x > y) return x - y; + else return y-x; +} + + uint16_t util::sat_cast(uint32_t x) { if(x > 0x0000FFFF) return 0xFFFF; diff --git a/firmware/src/utils/utils.h b/firmware/src/utils/utils.h index d411a15..1e45bfe 100644 --- a/firmware/src/utils/utils.h +++ b/firmware/src/utils/utils.h @@ -27,6 +27,10 @@ uint8_t sat_subtract(uint8_t x, uint8_t y); uint16_t sat_subtract(uint16_t x, uint16_t y); uint32_t sat_subtract(uint32_t x, uint32_t y); +uint8_t abs_subtract(uint8_t x, uint8_t y); +uint16_t abs_subtract(uint16_t x, uint16_t y); +uint32_t abs_subtract(uint32_t x, uint32_t y); + uint16_t interpolate_1d(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis); uint16_t interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values); -- 2.49.1 From 5bb3ebe1bf92e83dd1b056db3078605ae83534cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 11:51:21 +0300 Subject: [PATCH 17/35] Created current fuse logic --- firmware/src/hw/fuse.cpp | 67 ++++++++++++++++++++++++++++++++++++++ firmware/src/hw/fuse.h | 40 +++++++++++++++++++++++ firmware/src/uDCCD.cppproj | 6 ++++ 3 files changed, 113 insertions(+) create mode 100644 firmware/src/hw/fuse.cpp create mode 100644 firmware/src/hw/fuse.h diff --git a/firmware/src/hw/fuse.cpp b/firmware/src/hw/fuse.cpp new file mode 100644 index 0000000..3e75203 --- /dev/null +++ b/firmware/src/hw/fuse.cpp @@ -0,0 +1,67 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "../utils/interpolate.h" +#include "fuse.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::Fuse::Fuse(bsp::AnalogIn* ain_ch) +{ + this->hold_current = 0; + this->trip_cycles = 0; + this->warning = 0; + this->fault = 0; + this->cooldown_counter = 0; + this->cooldown_cycles = 0; + this->retry_cnt = 0; +} + +hw::Fuse::~Fuse(void) +{ + return; +} + +void hw::Fuse::update(void) +{ + // Under threshold + if(this->ain_ch->last_read <= this->hold_current) + { + // Clear warning flag + this->warning = 0; + + // OC energy counter + if(this->oc_counter > 0) this->oc_counter--; + + // Cool down fuse + if(this->cooldown_counter > 0) this->cooldown_counter--; + + // Auto reset logic + if((this->fault)&&(this->cooldown_counter==0)) + { + this->fault = 0; + this->retry_cnt = util::sat_add(this->retry_cnt, 1); + }; + return; + }; + + // Over current condition + this->warning = 1; + + // PC energy counter + this->oc_counter = util::sat_add(this->oc_counter, 1); + + // Check for trip threshold + if(this->oc_counter < this->trip_cycles) return; + + // Trip fuse + this->fault = 1; + this->cooldown_counter = this->cooldown_cycles; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/fuse.h b/firmware/src/hw/fuse.h new file mode 100644 index 0000000..ae789c8 --- /dev/null +++ b/firmware/src/hw/fuse.h @@ -0,0 +1,40 @@ +#ifndef FUSE_H_ +#define FUSE_H_ + +/**** Includes ****/ +#include +#include "../bsp/ain.h" + +namespace hw { + +/**** Public definitions ****/ + +class Fuse +{ + protected: + bsp::AnalogIn* ain_ch; + uint16_t oc_counter; + uint16_t cooldown_counter; + + public: + Fuse(bsp::AnalogIn* ain_ch); + ~Fuse(void); + + uint16_t hold_current; + uint16_t trip_cycles; + uint8_t warning; + uint8_t fault; + uint16_t cooldown_cycles; + uint8_t retry_cnt; + + void update(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* POTENTIOMETER_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index e57255b..31bffa1 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -213,6 +213,12 @@ compile + + compile + + + compile + compile -- 2.49.1 From f8b62d4b00268dcc7e3907896cb5a389f4479c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 15:18:31 +0300 Subject: [PATCH 18/35] Could be working version --- firmware/src/bsp/board.cpp | 62 ++++++++ firmware/src/bsp/board.h | 41 +++++ firmware/src/hw/button.cpp | 9 ++ firmware/src/hw/button.h | 1 + firmware/src/hw/cc_output.cpp | 41 +++++ firmware/src/hw/cc_output.h | 35 +++++ .../src/hw/{cv_otput.cpp => cv_output.cpp} | 0 firmware/src/hw/devices.cpp | 59 +++++++ firmware/src/hw/devices.h | 35 +++++ firmware/src/hw/fuse.cpp | 1 - firmware/src/logic/button_force.cpp | 51 ++++++ firmware/src/logic/button_force.h | 36 +++++ firmware/src/logic/dccd_force.cpp | 79 ++++++++++ firmware/src/logic/dccd_force.h | 39 +++++ firmware/src/main.cpp | 146 ++++++++++++------ firmware/src/uDCCD.cppproj | 33 +++- firmware/src/utils/interpolate.cpp | 6 +- firmware/src/utils/utils.cpp | 9 ++ firmware/src/utils/utils.h | 2 + 19 files changed, 632 insertions(+), 53 deletions(-) create mode 100644 firmware/src/bsp/board.cpp create mode 100644 firmware/src/bsp/board.h create mode 100644 firmware/src/hw/cc_output.cpp create mode 100644 firmware/src/hw/cc_output.h rename firmware/src/hw/{cv_otput.cpp => cv_output.cpp} (100%) create mode 100644 firmware/src/hw/devices.cpp create mode 100644 firmware/src/hw/devices.h create mode 100644 firmware/src/logic/button_force.cpp create mode 100644 firmware/src/logic/button_force.h create mode 100644 firmware/src/logic/dccd_force.cpp create mode 100644 firmware/src/logic/dccd_force.h diff --git a/firmware/src/bsp/board.cpp b/firmware/src/bsp/board.cpp new file mode 100644 index 0000000..2762cb6 --- /dev/null +++ b/firmware/src/bsp/board.cpp @@ -0,0 +1,62 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "board.h" + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +void board_init(void) +{ + // MCU setup + mcu::startupCfg_t mcu_cfg; + mcu_cfg.adc_clk = mcu::ADC_DIV2; + mcu_cfg.pwm_clk = mcu::TIM_DIV1; + mcu_cfg.pwm_top = 200; + mcu_cfg.pwm_ch1_en = 1; + + mcu::startup(&mcu_cfg); + + // Board setup + dccd_i.mul = 215; + dccd_i.div = 22; + dccd_i.offset = 0; + dccd_i.last_read = 0; + + dccd_u.mul = 20; + dccd_u.div = 1; + dccd_u.offset = 0; + dccd_u.last_read = 0; + + bat_u.mul = 20; + bat_u.div = 1; + bat_u.offset = 0; + bat_u.last_read = 12000; + + bat_i.mul = 235; + bat_i.div = 6; + bat_i.offset = 0; + bat_i.last_read = 0; +} + +void board_read(void) +{ + dccd_i.read(); + dccd_u.read(); + bat_u.read(); + bat_i.read(); + ain1.read(); + ain2.read(); + + din1.read(); + din2.read(); + din3.read(); + din4.read(); + hvdin1.read(); + hvdin2.read(); + hvdin3.read(); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/bsp/board.h b/firmware/src/bsp/board.h new file mode 100644 index 0000000..ab108f9 --- /dev/null +++ b/firmware/src/bsp/board.h @@ -0,0 +1,41 @@ +#ifndef BSP_BOARD_H_ +#define BSP_BOARD_H_ + +/**** Includes ****/ +#include + +#include "mcu/mcu_hal.h" +#include "ain.h" +#include "din.h" +#include "dout.h" +#include "dio.h" +#include "halfbridge.h" +#include "pwm.h" + +static bsp::AnalogIn dccd_i = bsp::AnalogIn(mcu::ADC0); +static bsp::AnalogIn dccd_u = bsp::AnalogIn(mcu::ADC1); +static bsp::AnalogIn bat_u = bsp::AnalogIn(mcu::ADC2); +static bsp::AnalogIn bat_i = bsp::AnalogIn(mcu::ADC3); +static bsp::Hafbridge hbridge = bsp::Hafbridge(mcu::PWM0, mcu::GPIO15, 95); +static bsp::AnalogIn ain1 = bsp::AnalogIn(mcu::ADC5); // mode +static bsp::AnalogIn ain2 = bsp::AnalogIn(mcu::ADC4); // pot +static bsp::DigitalIn din1 = bsp::DigitalIn(mcu::GPIO0, 0, bsp::DIN_HIGH); //mode +static bsp::DigitalIn din2 = bsp::DigitalIn(mcu::GPIO1, 0, bsp::DIN_HIGH); //pot +static bsp::DigitalIn din3 = bsp::DigitalIn(mcu::GPIO2, 0, bsp::DIN_HIGH); //down +static bsp::DigitalIn din4 = bsp::DigitalIn(mcu::GPIO3, 0, bsp::DIN_HIGH); //up +static bsp::DigitalIn hvdin1 = bsp::DigitalIn(mcu::GPIO4, 1, bsp::DIN_LOW); //dimm +static bsp::DigitalIn hvdin2 = bsp::DigitalIn(mcu::GPIO5, 1, bsp::DIN_LOW); //brakes +static bsp::DigitalIn hvdin3 = bsp::DigitalIn(mcu::GPIO6, 1, bsp::DIN_LOW); //hbrake +static bsp::DigitalIO hvdin3_pull = bsp::DigitalIO(mcu::GPIO7, bsp::DIN_HIGH); //hbrake pull +static bsp::DigitalOut odout1 = bsp::DigitalOut(mcu::GPIO9, 1); +static bsp::DigitalOut odout2 = bsp::DigitalOut(mcu::GPIO10, 1); +static bsp::DigitalOut odout3 = bsp::DigitalOut(mcu::GPIO11, 1); +static bsp::DigitalOut odout4 = bsp::DigitalOut(mcu::GPIO12, 1); +static bsp::DigitalOut odout5 = bsp::DigitalOut(mcu::GPIO13, 1); +static bsp::DigitalOut odout6 = bsp::DigitalOut(mcu::GPIO14, 1); +static bsp::PWMout od_pwm = bsp::PWMout(mcu::PWM1); + +void board_init(void); +void board_read(void); + +#endif /* BSP_BOARD_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/button.cpp b/firmware/src/hw/button.cpp index cf584b2..e1153c2 100644 --- a/firmware/src/hw/button.cpp +++ b/firmware/src/hw/button.cpp @@ -25,6 +25,8 @@ hw::Button::Button(bsp::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, ui this->time = 0; this->is_new = 0; + + this->hold_time = 0; } hw::Button::~Button(void) @@ -40,6 +42,13 @@ uint8_t hw::Button::update(void) // Increase state counter this->time = util::sat_add(this->time, 1); + // Repeat new flag after hold time + if((this->state == BUTTON_ON)&&(this->time > this->hold_time)&&(this->hold_time > 0)) + { + this->time = 0; + this->is_new = 1; + }; + // Determine next state uint8_t next_state = BUTTON_OFF; if(lvl==this->act_lvl) next_state = BUTTON_ON; diff --git a/firmware/src/hw/button.h b/firmware/src/hw/button.h index b77e549..e0f59e1 100644 --- a/firmware/src/hw/button.h +++ b/firmware/src/hw/button.h @@ -26,6 +26,7 @@ class Button uint16_t time; uint8_t dbnc_lim; uint8_t is_new; + uint16_t hold_time; uint8_t update(void); uint8_t force_update(void); diff --git a/firmware/src/hw/cc_output.cpp b/firmware/src/hw/cc_output.cpp new file mode 100644 index 0000000..bd3e713 --- /dev/null +++ b/firmware/src/hw/cc_output.cpp @@ -0,0 +1,41 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "cc_output.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::CCoutput::CCoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i) : CVoutput(hbridge, supply_u) +{ + this->out_voltage = out_u; + this->out_currnet = out_i; + + this->out_impedance = 0xFFFF; + this->target_voltage = 0; +} + +void hw::CCoutput::update(void) +{ + // Calculate output impedance + if((this->out_currnet == 0)||(this->out_voltage->last_read == 0)) this->out_impedance = 0xFFFF; + else + { + this->out_impedance = util::sat_div_kilo(this->out_voltage->last_read, this->out_currnet->last_read); + } + + // Check target + if((this->target < this->min_out)&&(this->target > 0)) this->target = this->min_out; + + // Convert target current to voltage + this->target_voltage = util::sat_mul_kilo(this->target, this->out_impedance); + + // Set output + this->hbridge->write(util::sat_ratio(this->target_voltage, this->supply->last_read)); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/cc_output.h b/firmware/src/hw/cc_output.h new file mode 100644 index 0000000..fee738f --- /dev/null +++ b/firmware/src/hw/cc_output.h @@ -0,0 +1,35 @@ +#ifndef CONST_CURRENT_OUTPUT_H_ +#define CONST_CURRENT_OUTPUT_H_ + +/**** Includes ****/ +#include +#include "../bsp/ain.h" +#include "cv_output.h" + +namespace hw { + +/**** Public definitions ****/ + +class CCoutput : public CVoutput +{ + protected: + bsp::AnalogIn* out_voltage; + bsp::AnalogIn* out_currnet; + + public: + CCoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i); + + void update(void); + + uint16_t out_impedance; + uint16_t target_voltage; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cv_otput.cpp b/firmware/src/hw/cv_output.cpp similarity index 100% rename from firmware/src/hw/cv_otput.cpp rename to firmware/src/hw/cv_output.cpp diff --git a/firmware/src/hw/devices.cpp b/firmware/src/hw/devices.cpp new file mode 100644 index 0000000..6c4bf62 --- /dev/null +++ b/firmware/src/hw/devices.cpp @@ -0,0 +1,59 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "devices.h" + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +void devices_init(void) +{ + board_init(); + + btn_up.hold_time = 1000; + btn_down.hold_time = 1000; + + ccout.target = 0; + ccout.min_out = 100; + + sup_fuse.hold_current = 6000; + sup_fuse.trip_cycles = 50; + sup_fuse.cooldown_cycles = 1000; + + out_fuse.hold_current = 6000; + out_fuse.trip_cycles = 100; + out_fuse.cooldown_cycles = 1000; + + hvdin3_pull.write(bsp::DOUT_HIGH); + + devices_update_inputs(); + + display.write(0x00); + display.set_brigthness(100); + + ccout.target = 0; + ccout.update(); + ccout.enable(); +} + +void devices_update_inputs(void) +{ + board_read(); + + pot.update(); + + btn_mode.update(); + btn_up.update(); + btn_down.update(); + + sw_dimm.update(); + sw_brakes.update(); + sw_hbrake.update(); + + sup_fuse.update(); + out_fuse.update(); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/devices.h b/firmware/src/hw/devices.h new file mode 100644 index 0000000..76d480a --- /dev/null +++ b/firmware/src/hw/devices.h @@ -0,0 +1,35 @@ +#ifndef HW_DEVICES_H_ +#define HW_DEVICES_H_ + +/**** Includes ****/ +#include + +#include "../bsp/board.h" + +#include "button.h" +#include "potentiometer.h" +#include "display_led.h" +#include "cv_output.h" +#include "cc_output.h" +#include "fuse.h" + +static hw::Button btn_mode = hw::Button(&din1, bsp::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_up = hw::Button(&din4, bsp::DIN_LOW, 10, hw::BUTTON_OFF); +static hw::Button btn_down = hw::Button(&din3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); + +static hw::Button sw_dimm = hw::Button(&hvdin1, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_brakes = hw::Button(&hvdin2, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); +static hw::Button sw_hbrake = hw::Button(&hvdin3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); + +static hw::Potentiometer pot = hw::Potentiometer(&ain2, 500, 4500); + +static hw::DisplayLed display = hw::DisplayLed(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); + +static hw::CCoutput ccout = hw::CCoutput(&hbridge, &bat_u, &dccd_u, &dccd_i); +static hw::Fuse sup_fuse = hw::Fuse(&bat_i); +static hw::Fuse out_fuse = hw::Fuse(&dccd_i); + +void devices_init(void); +void devices_update_inputs(void); + +#endif /* BSP_BOARD_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/fuse.cpp b/firmware/src/hw/fuse.cpp index 3e75203..51d8cae 100644 --- a/firmware/src/hw/fuse.cpp +++ b/firmware/src/hw/fuse.cpp @@ -1,6 +1,5 @@ /**** Includes ****/ #include "../utils/utils.h" -#include "../utils/interpolate.h" #include "fuse.h" using namespace hw; diff --git a/firmware/src/logic/button_force.cpp b/firmware/src/logic/button_force.cpp new file mode 100644 index 0000000..59f70ad --- /dev/null +++ b/firmware/src/logic/button_force.cpp @@ -0,0 +1,51 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "button_force.h" + +using namespace logic; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +logic::ButtonForce::ButtonForce(hw::Button* btn_up, hw::Button* btn_down) +{ + this->btn_up = btn_up; + this->btn_down = btn_down; + this->force = 0; + this->step = 10; + this->is_new = 0; +} + +logic::ButtonForce::~ButtonForce(void) +{ + return; +} + +uint8_t logic::ButtonForce::update(void) +{ + uint8_t next_force = 0; + + if((this->btn_up->is_new)&&(this->btn_up->state == hw::BUTTON_ON)) + { + next_force = util::sat_add(this->force, this->step); + if(next_force > 100) next_force = 100; + this->btn_up->is_new = 0; + }; + + if((this->btn_down->is_new)&&(this->btn_down->state == hw::BUTTON_ON)) + { + next_force = util::sat_subtract(this->force, this->step); + this->btn_down->is_new = 0; + }; + + if(next_force != this->force) this->is_new = 1; + + this->force = next_force; + + return this->force; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/logic/button_force.h b/firmware/src/logic/button_force.h new file mode 100644 index 0000000..4dd81e7 --- /dev/null +++ b/firmware/src/logic/button_force.h @@ -0,0 +1,36 @@ +#ifndef BUTTON_FORCE_H_ +#define BUTTON_FORCE_H_ + +/**** Includes ****/ +#include +#include "../hw/button.h" + +namespace logic { + +/**** Public definitions ****/ + +class ButtonForce +{ + protected: + hw::Button* btn_up; + hw::Button* btn_down; + + public: + ButtonForce(hw::Button* btn_up, hw::Button* btn_down); + ~ButtonForce(void); + + uint8_t force; + uint8_t step; + uint8_t is_new; + + uint8_t update(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* BUTTON_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/dccd_force.cpp b/firmware/src/logic/dccd_force.cpp new file mode 100644 index 0000000..9342f03 --- /dev/null +++ b/firmware/src/logic/dccd_force.cpp @@ -0,0 +1,79 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "dccd_force.h" + +using namespace logic; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +logic::DccdForce::DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::Button* sw_brakes) +{ + this->mode = btn_mode; + this->handbrake = sw_hbrake; + this->brakes = sw_brakes; + + this->is_new = 0; + this->force = 0; + this->brake_mode = 0; +} + +logic::DccdForce::~DccdForce(void) +{ + return; +} + +uint8_t logic::DccdForce::update(uint8_t user_force) +{ + // Process mode button + if((this->mode->is_new)&&(this->mode->state == hw::BUTTON_ON)) + { + // Cycle brake mode + switch(this->brake_mode) + { + case 0: + this->brake_mode = 1; + + case 1: + this->brake_mode = 2; + + default: + this->brake_mode = 0; + } + + this->mode->is_new = 0; + this->is_new_bmode = 1; + }; + + // Determine target force source + uint8_t next_force = user_force; + if(this->handbrake->state == hw::BUTTON_ON) + { + next_force = 0; + } + else if(this->brakes->state == hw::BUTTON_ON) + { + switch(this->brake_mode) + { + case 0: + next_force = 0; + + case 1: + next_force = user_force; + + default: + next_force = 100; + } + }; + + if(next_force != this->force) this->is_new = 1; + + this->force = next_force; + + return this->force; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/logic/dccd_force.h b/firmware/src/logic/dccd_force.h new file mode 100644 index 0000000..f4533cb --- /dev/null +++ b/firmware/src/logic/dccd_force.h @@ -0,0 +1,39 @@ +#ifndef DCCD_FORCE_H_ +#define DCCD_FORCE_H_ + +/**** Includes ****/ +#include +#include "../hw/button.h" + +namespace logic { + +/**** Public definitions ****/ + +class DccdForce +{ + protected: + hw::Button* mode; + hw::Button* handbrake; + hw::Button* brakes; + + public: + DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::Button* sw_brakes); + ~DccdForce(void); + + uint8_t force; + uint8_t is_new; + + uint8_t brake_mode; + uint8_t is_new_bmode; + + uint8_t update(uint8_t user_force); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DCCD_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index b730192..5975207 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,66 +1,116 @@ /**** Includes ****/ #include "utils/utils.h" +#include "utils/interpolate.h" -#include "bsp/mcu/mcu_hal.h" -#include "bsp/ain.h" -#include "bsp/din.h" -#include "bsp/dout.h" -#include "bsp/dio.h" -#include "bsp/halfbridge.h" -#include "bsp/pwm.h" +#include "hw/devices.h" -#include "hw/button.h" -#include "hw/potentiometer.h" -#include "hw/display_led.h" -#include "hw/cv_output.h" +#include "logic/button_force.h" +#include "logic/dccd_force.h" /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ -static bsp::AnalogIn dccd_i(mcu::ADC0); -static bsp::AnalogIn dccd_u(mcu::ADC1); -static bsp::AnalogIn bat_u(mcu::ADC2); -static bsp::AnalogIn bat_i(mcu::ADC3); -static bsp::Hafbridge hbridge(mcu::PWM0, mcu::GPIO15, 95); -static bsp::AnalogIn ain1(mcu::ADC5); // mode -static bsp::AnalogIn ain2(mcu::ADC4); // pot -static bsp::DigitalIn din1(mcu::GPIO0, 0, bsp::DIN_HIGH); //mode -static bsp::DigitalIn din2(mcu::GPIO1, 0, bsp::DIN_HIGH); //pot -static bsp::DigitalIn din3(mcu::GPIO2, 0, bsp::DIN_HIGH); //down -static bsp::DigitalIn din4(mcu::GPIO3, 0, bsp::DIN_HIGH); //up -static bsp::DigitalIn hvdin1(mcu::GPIO4, 1, bsp::DIN_LOW); //dimm -static bsp::DigitalIn hvdin2(mcu::GPIO5, 1, bsp::DIN_LOW); //brakes -static bsp::DigitalIn hvdin3(mcu::GPIO6, 1, bsp::DIN_LOW); //hbrake -static bsp::DigitalIO hvdin3_pull(mcu::GPIO7, bsp::DIN_HIGH); //hbrake pull -static bsp::DigitalOut odout1(mcu::GPIO9, 1); -static bsp::DigitalOut odout2(mcu::GPIO10, 1); -static bsp::DigitalOut odout3(mcu::GPIO11, 1); -static bsp::DigitalOut odout4(mcu::GPIO12, 1); -static bsp::DigitalOut odout5(mcu::GPIO13, 1); -static bsp::DigitalOut odout6(mcu::GPIO14, 1); -static bsp::PWMout od_pwm(mcu::PWM1); - -static hw::Button btn_mode(&din1, bsp::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_up(&din4, bsp::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_down(&din3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); - -static hw::Button sw_dimm(&hvdin1, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_brakes(&hvdin2, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_hbrake(&hvdin3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); - -static hw::Potentiometer pot(&ain2, 500, 4500); - -static hw::DisplayLed display(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); - -static hw::CVoutput cvout(&hbridge, &bat_u); +static logic::ButtonForce button_force = logic::ButtonForce(&btn_up, &btn_down); +static logic::DccdForce dccd_force = logic::DccdForce(&btn_mode, &sw_hbrake, &sw_brakes); /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) { + // HW setup + devices_init(); + + uint8_t user_force = 0; + + button_force.force = mcu::eeprom_read8b(0x0000); + dccd_force.brake_mode = mcu::eeprom_read8b(0x0001); + uint8_t pot_mode = mcu::eeprom_read8b(0x0002); + uint8_t dsp_brigth = mcu::eeprom_read8b(0x0003); + uint8_t dsp_dimm = mcu::eeprom_read8b(0x0004); + uint16_t lock_current = mcu::eeprom_read16b(0x0005); + + // Safeguard eeprom values + if(button_force.force > 100) + { + button_force.force = 0; + mcu::eeprom_write8b(0x0000, button_force.force); + }; + + if(dccd_force.brake_mode > 2) + { + button_force.force = 0; + mcu::eeprom_write8b(0x0001, dccd_force.brake_mode); + }; + + if(pot_mode > 1) + { + pot_mode = 0; + mcu::eeprom_write8b(0x0002, pot_mode); + }; + + if(dsp_brigth > 100) + { + dsp_brigth = 100; + mcu::eeprom_write8b(0x0003, dsp_brigth); + }; + + if(dsp_dimm > 100) + { + dsp_dimm = 50; + mcu::eeprom_write8b(0x0004, dsp_dimm); + }; + + if(lock_current > 6000) + { + lock_current = 4500; + mcu::eeprom_write16b(0x0005, lock_current); + }; + // Super loop while(1) { + // Update inputs + devices_update_inputs(); + + // Update user setting + button_force.update(); + + // Select user force input + if(pot_mode) user_force = pot.percent; + else user_force = button_force.force; + + // Calculate next target force + dccd_force.update(user_force); + + // Override force in case of fault + if((sup_fuse.fault)||(out_fuse.fault)) dccd_force.force = 0; + + // Convert force to current + ccout.target = util::percent_of(dccd_force.force, lock_current); + + // Execute outputs + ccout.update(); + + // Set display + display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); + + // Process dimm + if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(dsp_dimm); + else display.set_brigthness(dsp_brigth); + + // Save user setting changes + if(button_force.is_new) + { + mcu::eeprom_write8b(0x0000, button_force.force); + button_force.is_new = 0; + }; + + if(dccd_force.is_new_bmode) + { + mcu::eeprom_write8b(0x0001, dccd_force.brake_mode); + dccd_force.is_new_bmode = 0; + }; + continue; // End of super loop } @@ -68,4 +118,4 @@ int main(void) return 0; } -/**** Private function definitions ***/ +/**** Private function definitions ***/ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 31bffa1..cf0e235 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -159,6 +159,12 @@ compile + + compile + + + compile + compile @@ -201,7 +207,13 @@ compile - + + compile + + + compile + + compile @@ -219,12 +231,30 @@ compile + + compile + + + compile + compile compile + + compile + + + compile + + + compile + + + compile + compile @@ -245,6 +275,7 @@ + diff --git a/firmware/src/utils/interpolate.cpp b/firmware/src/utils/interpolate.cpp index fee0cfc..a9af4c7 100644 --- a/firmware/src/utils/interpolate.cpp +++ b/firmware/src/utils/interpolate.cpp @@ -131,7 +131,7 @@ uint16_t util::interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t return interpolate(y, y0, y1, zy0, zy1); } -uint16_t interpolate(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1) +uint16_t util::interpolate(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t y1) { int32_t dy = (int32_t)y1 - (int32_t)y0; int32_t dx = (int32_t)x1 - (int32_t)x0; @@ -144,7 +144,7 @@ uint16_t interpolate(uint16_t x, uint16_t x0, uint16_t x1, uint16_t y0, uint16_t return util::sat_cast(y); } -uint8_t find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis) +uint8_t util::find_interval_end_index(uint16_t val, uint16_t* axis_values, uint8_t len_axis) { for(uint8_t i=0; i= 100) return value; + + uint32_t temp = (uint32_t)value * percent; + return temp/100; +} + /**** Private function definitions ****/ \ No newline at end of file diff --git a/firmware/src/utils/utils.h b/firmware/src/utils/utils.h index 1e45bfe..9f16bb9 100644 --- a/firmware/src/utils/utils.h +++ b/firmware/src/utils/utils.h @@ -19,6 +19,8 @@ uint16_t sat_div_kilo(uint16_t top, uint16_t bot); uint16_t sat_ratio(uint16_t top, uint16_t bot); uint16_t percent_to_16b(uint8_t percent); +uint16_t percent_of(uint8_t percent, uint16_t value); + uint8_t sat_add(uint8_t x, uint8_t y); uint16_t sat_add(uint16_t x, uint16_t y); uint32_t sat_add(uint32_t x, uint32_t y); -- 2.49.1 From 0a2a040cf0f63743e87959150ebae46903ae13a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 17:16:58 +0300 Subject: [PATCH 19/35] Added memory config class --- firmware/src/logic/cfg_mem.cpp | 138 +++++++++++++++++++++++++++++++++ firmware/src/logic/cfg_mem.h | 46 +++++++++++ firmware/src/main.cpp | 74 ++++-------------- firmware/src/uDCCD.cppproj | 10 ++- 4 files changed, 208 insertions(+), 60 deletions(-) create mode 100644 firmware/src/logic/cfg_mem.cpp create mode 100644 firmware/src/logic/cfg_mem.h diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp new file mode 100644 index 0000000..8196dda --- /dev/null +++ b/firmware/src/logic/cfg_mem.cpp @@ -0,0 +1,138 @@ +/**** Includes ****/ +#include "cfg_mem.h" +#include "../bsp/mcu/mcu_hal.h" + +using namespace logic; + +/**** Private definitions ****/ +/**** Private constants ****/ +const uint16_t addr_btn_force = 0x0000; +const uint16_t addr_bmode = 0x0001; +const uint16_t addr_pot_mode = 0x0002; +const uint16_t addr_dsp_brigth = 0x0003; +const uint16_t addr_dsp_dimm = 0x0004; +const uint16_t addr_lock_current = 0x0005; + +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +logic::CfgMemory::CfgMemory(void) +{ + this->mem_btn_force = 0; + this->mem_bmode = 0; + this->mem_pot_mode = 0; + this->mem_dsp_brigth = 0; + this->mem_dsp_dimm = 0; + this->mem_lock_current = 0; + + this->restore(); +} + +logic::CfgMemory::~CfgMemory(void) +{ + return; +} + +void logic::CfgMemory::init(void) +{ + this->mem_btn_force = mcu::eeprom_read8b(addr_btn_force); + this->mem_bmode = mcu::eeprom_read8b(addr_bmode); + this->mem_pot_mode = mcu::eeprom_read8b(addr_pot_mode); + this->mem_dsp_brigth = mcu::eeprom_read8b(addr_dsp_brigth); + this->mem_dsp_dimm = mcu::eeprom_read8b(addr_dsp_dimm); + this->mem_lock_current = mcu::eeprom_read16b(addr_lock_current); + + if(this->mem_btn_force > 100) + { + this->mem_btn_force = 0; + mcu::eeprom_write8b(addr_btn_force, this->mem_btn_force); + }; + + if(this->mem_bmode > 2) + { + this->mem_bmode = 0; + mcu::eeprom_write8b(addr_bmode, this->mem_bmode); + }; + + if(this->mem_pot_mode > 1) + { + this->mem_pot_mode = 0; + mcu::eeprom_write8b(addr_pot_mode, this->mem_pot_mode); + }; + + if(this->mem_dsp_brigth > 100) + { + this->mem_dsp_brigth = 100; + mcu::eeprom_write8b(addr_dsp_brigth, this->mem_dsp_brigth); + }; + + if(this->mem_dsp_dimm > 100) + { + this->mem_dsp_dimm = 50; + mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); + }; + + if(this->mem_lock_current > 6000) + { + this->mem_lock_current = 4500; + mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); + }; + + this->restore(); +} + +void logic::CfgMemory::save(void) +{ + if(this->btn_force != this->mem_btn_force) + { + this->mem_btn_force = this->btn_force; + mcu::eeprom_write8b(addr_btn_force, this->mem_btn_force); + }; + + if(this->bmode != this->mem_bmode) + { + this->mem_bmode = this->bmode; + mcu::eeprom_write8b(addr_bmode, this->mem_bmode); + }; +} + +void logic::CfgMemory::save_all(void) +{ + this->save(); + + if(this->pot_mode != this->mem_pot_mode) + { + this->mem_pot_mode = this->pot_mode; + mcu::eeprom_write8b(addr_pot_mode, this->mem_pot_mode); + }; + + if(this->dsp_brigth != this->mem_dsp_brigth) + { + this->mem_dsp_brigth = this->dsp_brigth; + mcu::eeprom_write8b(addr_dsp_brigth, this->mem_dsp_brigth); + }; + + if(this->dsp_dimm != this->mem_dsp_dimm) + { + this->mem_dsp_dimm = this->dsp_dimm; + mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); + }; + + if(this->lock_current != this->mem_lock_current) + { + this->mem_lock_current = this->lock_current; + mcu::eeprom_write8b(addr_lock_current, this->mem_lock_current); + }; +} + +void logic::CfgMemory::restore(void) +{ + this->btn_force = this->mem_btn_force; + this->bmode = this->mem_bmode; + this->pot_mode = this->mem_pot_mode; + this->dsp_brigth = this->mem_dsp_brigth; + this->dsp_dimm = this->mem_dsp_dimm; + this->lock_current = this->mem_lock_current; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/logic/cfg_mem.h b/firmware/src/logic/cfg_mem.h new file mode 100644 index 0000000..45b513e --- /dev/null +++ b/firmware/src/logic/cfg_mem.h @@ -0,0 +1,46 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/**** Includes ****/ +#include +#include "../hw/button.h" + +namespace logic { + +/**** Public definitions ****/ + +class CfgMemory +{ + protected: + uint8_t mem_btn_force; + uint8_t mem_bmode; + uint8_t mem_pot_mode; + uint8_t mem_dsp_brigth; + uint8_t mem_dsp_dimm; + uint16_t mem_lock_current; + + public: + CfgMemory(void); + ~CfgMemory(void); + + uint8_t btn_force; + uint8_t bmode; + uint8_t pot_mode; + uint8_t dsp_brigth; + uint8_t dsp_dimm; + uint16_t lock_current; + + void init(void); + void save(void); + void save_all(void); + void restore(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DCCD_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 5975207..7d723ee 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -7,9 +7,13 @@ #include "logic/button_force.h" #include "logic/dccd_force.h" +#include "logic/cfg_mem.h" + /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ +static logic::CfgMemory cfg_mem = logic::CfgMemory(); + static logic::ButtonForce button_force = logic::ButtonForce(&btn_up, &btn_down); static logic::DccdForce dccd_force = logic::DccdForce(&btn_mode, &sw_hbrake, &sw_brakes); @@ -20,51 +24,13 @@ int main(void) // HW setup devices_init(); + // Read saved config + cfg_mem.init(); + uint8_t user_force = 0; - button_force.force = mcu::eeprom_read8b(0x0000); - dccd_force.brake_mode = mcu::eeprom_read8b(0x0001); - uint8_t pot_mode = mcu::eeprom_read8b(0x0002); - uint8_t dsp_brigth = mcu::eeprom_read8b(0x0003); - uint8_t dsp_dimm = mcu::eeprom_read8b(0x0004); - uint16_t lock_current = mcu::eeprom_read16b(0x0005); - - // Safeguard eeprom values - if(button_force.force > 100) - { - button_force.force = 0; - mcu::eeprom_write8b(0x0000, button_force.force); - }; - - if(dccd_force.brake_mode > 2) - { - button_force.force = 0; - mcu::eeprom_write8b(0x0001, dccd_force.brake_mode); - }; - - if(pot_mode > 1) - { - pot_mode = 0; - mcu::eeprom_write8b(0x0002, pot_mode); - }; - - if(dsp_brigth > 100) - { - dsp_brigth = 100; - mcu::eeprom_write8b(0x0003, dsp_brigth); - }; - - if(dsp_dimm > 100) - { - dsp_dimm = 50; - mcu::eeprom_write8b(0x0004, dsp_dimm); - }; - - if(lock_current > 6000) - { - lock_current = 4500; - mcu::eeprom_write16b(0x0005, lock_current); - }; + button_force.force = cfg_mem.btn_force; + dccd_force.brake_mode = cfg_mem.bmode; // Super loop while(1) @@ -76,7 +42,7 @@ int main(void) button_force.update(); // Select user force input - if(pot_mode) user_force = pot.percent; + if(cfg_mem.pot_mode) user_force = pot.percent; else user_force = button_force.force; // Calculate next target force @@ -86,7 +52,7 @@ int main(void) if((sup_fuse.fault)||(out_fuse.fault)) dccd_force.force = 0; // Convert force to current - ccout.target = util::percent_of(dccd_force.force, lock_current); + ccout.target = util::percent_of(dccd_force.force, cfg_mem.lock_current); // Execute outputs ccout.update(); @@ -95,21 +61,13 @@ int main(void) display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); // Process dimm - if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(dsp_dimm); - else display.set_brigthness(dsp_brigth); + if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(cfg_mem.dsp_dimm); + else display.set_brigthness(cfg_mem.dsp_brigth); // Save user setting changes - if(button_force.is_new) - { - mcu::eeprom_write8b(0x0000, button_force.force); - button_force.is_new = 0; - }; - - if(dccd_force.is_new_bmode) - { - mcu::eeprom_write8b(0x0001, dccd_force.brake_mode); - dccd_force.is_new_bmode = 0; - }; + cfg_mem.btn_force = button_force.force; + cfg_mem.bmode = dccd_force.brake_mode; + cfg_mem.save(); continue; // End of super loop } diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index cf0e235..a4b7be9 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -20,10 +20,10 @@ false true true - + 0x20000000 true - + exception_table 2 0 0 @@ -249,6 +249,12 @@ compile + + compile + + + compile + compile -- 2.49.1 From 2647e1cf14c533ceeef04084a7c7c35efc05b3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 17:26:09 +0300 Subject: [PATCH 20/35] Added display user setting persist --- firmware/src/main.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 7d723ee..13f8f0a 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -28,6 +28,7 @@ int main(void) cfg_mem.init(); uint8_t user_force = 0; + uint16_t dsp_lock = 0; button_force.force = cfg_mem.btn_force; dccd_force.brake_mode = cfg_mem.bmode; @@ -58,7 +59,38 @@ int main(void) ccout.update(); // Set display - display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); + if(dccd_force.is_new_bmode) + { + uint8_t bmode_img = 0x03; + switch(dccd_force.brake_mode) + { + case 1: + bmode_img = 0x0C; + break; + + case 2: + bmode_img = 0x30; + break; + + default: + bmode_img = 0x03; + break; + } + display.write(bmode_img); + dsp_lock = 1000; + dccd_force.is_new_bmode = 0; + } + else if((button_force.is_new)&&(cfg_mem.pot_mode==0)) + { + display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); + dsp_lock = 500; + button_force.is_new = 0; + } + else + { + if(dsp_lock) dsp_lock--; + else display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); + } // Process dimm if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(cfg_mem.dsp_dimm); -- 2.49.1 From 82838c25cb8a9fe151c02221681fc96befcb3e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 17:33:13 +0300 Subject: [PATCH 21/35] Reduced magic numbers --- firmware/src/bsp/board.cpp | 1 + firmware/src/hw/devices.cpp | 26 +++++++++++++++----------- firmware/src/logic/cfg_mem.cpp | 31 +++++++++++++++++++------------ firmware/src/main.cpp | 7 +++++-- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/firmware/src/bsp/board.cpp b/firmware/src/bsp/board.cpp index 2762cb6..83216bc 100644 --- a/firmware/src/bsp/board.cpp +++ b/firmware/src/bsp/board.cpp @@ -20,6 +20,7 @@ void board_init(void) mcu::startup(&mcu_cfg); // Board setup + // Fixed function AIN mV and mA scale dccd_i.mul = 215; dccd_i.div = 22; dccd_i.offset = 0; diff --git a/firmware/src/hw/devices.cpp b/firmware/src/hw/devices.cpp index 6c4bf62..650df83 100644 --- a/firmware/src/hw/devices.cpp +++ b/firmware/src/hw/devices.cpp @@ -4,27 +4,32 @@ /**** Private definitions ****/ /**** Private constants ****/ +static const uint16_t def_button_hold_time = 1000; +static const uint16_t def_min_current = 100; +static const uint16_t def_fuse_treshold = 6000; +static const uint16_t def_fuse_hold_cycles = 50; +static const uint16_t def_fuse_cooldown_cycles = 1000; + /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ void devices_init(void) { board_init(); - btn_up.hold_time = 1000; - btn_down.hold_time = 1000; + btn_up.hold_time = def_button_hold_time; + btn_down.hold_time = def_button_hold_time; ccout.target = 0; - ccout.min_out = 100; + ccout.min_out = def_min_current; - sup_fuse.hold_current = 6000; - sup_fuse.trip_cycles = 50; - sup_fuse.cooldown_cycles = 1000; + sup_fuse.hold_current = def_fuse_treshold; + sup_fuse.trip_cycles = def_fuse_hold_cycles; + sup_fuse.cooldown_cycles = def_fuse_cooldown_cycles; - out_fuse.hold_current = 6000; - out_fuse.trip_cycles = 100; - out_fuse.cooldown_cycles = 1000; + out_fuse.hold_current = def_fuse_treshold; + out_fuse.trip_cycles = def_fuse_hold_cycles; + out_fuse.cooldown_cycles = def_fuse_cooldown_cycles; hvdin3_pull.write(bsp::DOUT_HIGH); @@ -33,7 +38,6 @@ void devices_init(void) display.write(0x00); display.set_brigthness(100); - ccout.target = 0; ccout.update(); ccout.enable(); } diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp index 8196dda..6cee6a1 100644 --- a/firmware/src/logic/cfg_mem.cpp +++ b/firmware/src/logic/cfg_mem.cpp @@ -6,12 +6,19 @@ using namespace logic; /**** Private definitions ****/ /**** Private constants ****/ -const uint16_t addr_btn_force = 0x0000; -const uint16_t addr_bmode = 0x0001; -const uint16_t addr_pot_mode = 0x0002; -const uint16_t addr_dsp_brigth = 0x0003; -const uint16_t addr_dsp_dimm = 0x0004; -const uint16_t addr_lock_current = 0x0005; +static const uint16_t addr_btn_force = 0x0000; +static const uint16_t addr_bmode = 0x0001; +static const uint16_t addr_pot_mode = 0x0002; +static const uint16_t addr_dsp_brigth = 0x0003; +static const uint16_t addr_dsp_dimm = 0x0004; +static const uint16_t addr_lock_current = 0x0005; + +static const uint8_t def_btn_force = 0; +static const uint8_t def_pot_mode = 0; +static const uint8_t def_bmode = 0; +static const uint8_t def_dimm = 50; +static const uint8_t def_brigth = 100; +static const uint16_t def_lock_current = 4500; /**** Private variables ****/ /**** Private function declarations ****/ @@ -44,37 +51,37 @@ void logic::CfgMemory::init(void) if(this->mem_btn_force > 100) { - this->mem_btn_force = 0; + this->mem_btn_force = def_btn_force; mcu::eeprom_write8b(addr_btn_force, this->mem_btn_force); }; if(this->mem_bmode > 2) { - this->mem_bmode = 0; + this->mem_bmode = def_bmode; mcu::eeprom_write8b(addr_bmode, this->mem_bmode); }; if(this->mem_pot_mode > 1) { - this->mem_pot_mode = 0; + this->mem_pot_mode = def_pot_mode; mcu::eeprom_write8b(addr_pot_mode, this->mem_pot_mode); }; if(this->mem_dsp_brigth > 100) { - this->mem_dsp_brigth = 100; + this->mem_dsp_brigth = def_brigth; mcu::eeprom_write8b(addr_dsp_brigth, this->mem_dsp_brigth); }; if(this->mem_dsp_dimm > 100) { - this->mem_dsp_dimm = 50; + this->mem_dsp_dimm = def_dimm; mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); }; if(this->mem_lock_current > 6000) { - this->mem_lock_current = 4500; + this->mem_lock_current = def_lock_current; mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); }; diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 13f8f0a..f3efffe 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -11,6 +11,9 @@ /**** Private definitions ****/ /**** Private constants ****/ +static const uint16_t dsp_lock_bmode = 1000; +static const uint16_t dsp_lock_force = 50; + /**** Private variables ****/ static logic::CfgMemory cfg_mem = logic::CfgMemory(); @@ -77,13 +80,13 @@ int main(void) break; } display.write(bmode_img); - dsp_lock = 1000; + dsp_lock = dsp_lock_bmode; dccd_force.is_new_bmode = 0; } else if((button_force.is_new)&&(cfg_mem.pot_mode==0)) { display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); - dsp_lock = 500; + dsp_lock = dsp_lock_force; button_force.is_new = 0; } else -- 2.49.1 From e7797e8d1cc04d1021d8cdb6b88d6ee1378dda2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 12 Apr 2024 17:40:00 +0300 Subject: [PATCH 22/35] Added hbrake timeout and adjustable brake force --- firmware/src/logic/dccd_force.cpp | 11 ++++++++--- firmware/src/logic/dccd_force.h | 3 +++ firmware/src/main.cpp | 5 +++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/firmware/src/logic/dccd_force.cpp b/firmware/src/logic/dccd_force.cpp index 9342f03..ae0493b 100644 --- a/firmware/src/logic/dccd_force.cpp +++ b/firmware/src/logic/dccd_force.cpp @@ -6,9 +6,11 @@ using namespace logic; /**** Private definitions ****/ /**** Private constants ****/ +static const uint16_t def_max_hbrake_time = 1000; +static const uint16_t def_brake_force = 100; + /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ logic::DccdForce::DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::Button* sw_brakes) { @@ -19,6 +21,9 @@ logic::DccdForce::DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::But this->is_new = 0; this->force = 0; this->brake_mode = 0; + + this->max_hbrake_time = def_max_hbrake_time; + this->brake_force = def_brake_force; } logic::DccdForce::~DccdForce(void) @@ -50,7 +55,7 @@ uint8_t logic::DccdForce::update(uint8_t user_force) // Determine target force source uint8_t next_force = user_force; - if(this->handbrake->state == hw::BUTTON_ON) + if((this->handbrake->state == hw::BUTTON_ON)&&(this->handbrake->time < this->max_hbrake_time)) { next_force = 0; } @@ -65,7 +70,7 @@ uint8_t logic::DccdForce::update(uint8_t user_force) next_force = user_force; default: - next_force = 100; + next_force = this->brake_force; } }; diff --git a/firmware/src/logic/dccd_force.h b/firmware/src/logic/dccd_force.h index f4533cb..c5291af 100644 --- a/firmware/src/logic/dccd_force.h +++ b/firmware/src/logic/dccd_force.h @@ -26,6 +26,9 @@ class DccdForce uint8_t brake_mode; uint8_t is_new_bmode; + uint16_t max_hbrake_time; + uint8_t brake_force; + uint8_t update(uint8_t user_force); }; diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index f3efffe..842f2ca 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -14,6 +14,9 @@ static const uint16_t dsp_lock_bmode = 1000; static const uint16_t dsp_lock_force = 50; +static const uint16_t cfg_max_hbrake_time = 1000; +static const uint8_t cfg_brake_force = 100; + /**** Private variables ****/ static logic::CfgMemory cfg_mem = logic::CfgMemory(); @@ -35,6 +38,8 @@ int main(void) button_force.force = cfg_mem.btn_force; dccd_force.brake_mode = cfg_mem.bmode; + dccd_force.max_hbrake_time = cfg_max_hbrake_time; + dccd_force.brake_force = cfg_brake_force; // Super loop while(1) -- 2.49.1 From 57ab8bda6a0e51a29d80a38bef9cbaec6cc91beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 12:27:29 +0300 Subject: [PATCH 23/35] Interpolation bug fix --- firmware/src/hw/potentiometer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/src/hw/potentiometer.cpp b/firmware/src/hw/potentiometer.cpp index 6559b2b..617a1b5 100644 --- a/firmware/src/hw/potentiometer.cpp +++ b/firmware/src/hw/potentiometer.cpp @@ -27,7 +27,9 @@ hw::Potentiometer::~Potentiometer(void) uint8_t hw::Potentiometer::update(void) { // Calculate percent - this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); + if(this->ain_ch->last_read <= this->low_deadzone) this->percent = 0; + else if(this->ain_ch->last_read >= this->high_deadzone ) this->percent = 100; + else this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); return this->percent; } -- 2.49.1 From 8449ca098e4635b195edddab3b1713ca926a73ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 12:27:58 +0300 Subject: [PATCH 24/35] Added lock --- firmware/src/hw/display_led.cpp | 15 +++++++++++++++ firmware/src/hw/display_led.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/firmware/src/hw/display_led.cpp b/firmware/src/hw/display_led.cpp index 801d0da..af9085b 100644 --- a/firmware/src/hw/display_led.cpp +++ b/firmware/src/hw/display_led.cpp @@ -30,6 +30,9 @@ hw::DisplayLed::DisplayLed(bsp::DigitalOut* led0, bsp::DigitalOut* led1, bsp::Di this->led4->write(0); this->led5->write(0); this->common->write(0); + + this->lock_counter = 0; + this->locked = 1; } hw::DisplayLed::~DisplayLed(void) @@ -91,6 +94,18 @@ void hw::DisplayLed::set_brigthness(uint8_t percent) this->common->write(percent); } +void hw::DisplayLed::set_lock(uint16_t timeout) +{ + this->lock_counter = timeout; + this->locked = 1; +} + +void hw::DisplayLed::process_timer(void) +{ + if(this->lock_counter) this->lock_counter--; + else this->locked = 0; +} + /**** Private function definitions ****/ static uint8_t img_gen_dot10(uint8_t percent) { diff --git a/firmware/src/hw/display_led.h b/firmware/src/hw/display_led.h index 455e5ad..6e73567 100644 --- a/firmware/src/hw/display_led.h +++ b/firmware/src/hw/display_led.h @@ -21,6 +21,8 @@ class DisplayLed bsp::DigitalOut* led5; bsp::PWMout* common; + uint16_t lock_counter; + public: typedef enum { LED_DSP_DOT20, @@ -35,6 +37,11 @@ class DisplayLed void write(uint8_t image); void set_brigthness(uint8_t percent); + + void set_lock(uint16_t timeout); + void process_timer(void); + + uint8_t locked; }; /**** Public function declarations ****/ -- 2.49.1 From a3d4ffd548b9fbf2f0faa26cef33884a58811fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 12:28:30 +0300 Subject: [PATCH 25/35] Handbrake timeout fix --- firmware/src/logic/dccd_force.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/src/logic/dccd_force.cpp b/firmware/src/logic/dccd_force.cpp index ae0493b..cedebe4 100644 --- a/firmware/src/logic/dccd_force.cpp +++ b/firmware/src/logic/dccd_force.cpp @@ -55,7 +55,7 @@ uint8_t logic::DccdForce::update(uint8_t user_force) // Determine target force source uint8_t next_force = user_force; - if((this->handbrake->state == hw::BUTTON_ON)&&(this->handbrake->time < this->max_hbrake_time)) + if((this->handbrake->state == hw::BUTTON_ON)&&((this->handbrake->time < this->max_hbrake_time)||(this->max_hbrake_time == 0))) { next_force = 0; } -- 2.49.1 From c3bc42fa1818a7c5f4011494e207357ad58c9a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 12:28:47 +0300 Subject: [PATCH 26/35] Added dccd logic config --- firmware/src/logic/cfg_mem.cpp | 72 ++++++++++++++++++++++++++-------- firmware/src/logic/cfg_mem.h | 4 ++ 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp index 6cee6a1..553e7de 100644 --- a/firmware/src/logic/cfg_mem.cpp +++ b/firmware/src/logic/cfg_mem.cpp @@ -6,20 +6,26 @@ using namespace logic; /**** Private definitions ****/ /**** Private constants ****/ -static const uint16_t addr_btn_force = 0x0000; -static const uint16_t addr_bmode = 0x0001; -static const uint16_t addr_pot_mode = 0x0002; -static const uint16_t addr_dsp_brigth = 0x0003; -static const uint16_t addr_dsp_dimm = 0x0004; -static const uint16_t addr_lock_current = 0x0005; +static const uint16_t addr_btn_force = 0x0000; +static const uint16_t addr_bmode = 0x0001; +static const uint16_t addr_pot_mode = 0x0002; +static const uint16_t addr_dsp_brigth = 0x0003; +static const uint16_t addr_dsp_dimm = 0x0004; +static const uint16_t addr_brake_force = 0x0005; +static const uint16_t addr_max_hbrake_time = 0x0006; +static const uint16_t addr_lock_current = 0x0008; static const uint8_t def_btn_force = 0; static const uint8_t def_pot_mode = 0; static const uint8_t def_bmode = 0; static const uint8_t def_dimm = 50; static const uint8_t def_brigth = 100; +static const uint8_t def_brake_force = 100; +static const uint16_t def_max_hbrake_time = 1000; static const uint16_t def_lock_current = 4500; +static const uint16_t max_lock_current = 6000; + /**** Private variables ****/ /**** Private function declarations ****/ /**** Public function definitions ****/ @@ -30,7 +36,9 @@ logic::CfgMemory::CfgMemory(void) this->mem_pot_mode = 0; this->mem_dsp_brigth = 0; this->mem_dsp_dimm = 0; - this->mem_lock_current = 0; + this->mem_brake_force = 0; + this->mem_max_hbrake_time = 0; + this->mem_lock_current = 0; this->restore(); } @@ -42,13 +50,16 @@ logic::CfgMemory::~CfgMemory(void) void logic::CfgMemory::init(void) { - this->mem_btn_force = mcu::eeprom_read8b(addr_btn_force); - this->mem_bmode = mcu::eeprom_read8b(addr_bmode); - this->mem_pot_mode = mcu::eeprom_read8b(addr_pot_mode); - this->mem_dsp_brigth = mcu::eeprom_read8b(addr_dsp_brigth); - this->mem_dsp_dimm = mcu::eeprom_read8b(addr_dsp_dimm); - this->mem_lock_current = mcu::eeprom_read16b(addr_lock_current); + this->mem_btn_force = mcu::eeprom_read8b(addr_btn_force); + this->mem_bmode = mcu::eeprom_read8b(addr_bmode); + this->mem_pot_mode = mcu::eeprom_read8b(addr_pot_mode); + this->mem_dsp_brigth = mcu::eeprom_read8b(addr_dsp_brigth); + this->mem_dsp_dimm = mcu::eeprom_read8b(addr_dsp_dimm); + this->mem_brake_force = mcu::eeprom_read8b(addr_brake_force); + this->mem_max_hbrake_time = mcu::eeprom_read16b(addr_max_hbrake_time); + this->mem_lock_current = mcu::eeprom_read16b(addr_lock_current); + // Validate EEPROM data if(this->mem_btn_force > 100) { this->mem_btn_force = def_btn_force; @@ -79,7 +90,22 @@ void logic::CfgMemory::init(void) mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); }; - if(this->mem_lock_current > 6000) + if(this->mem_brake_force > 100) + { + this->mem_brake_force = def_brake_force; + mcu::eeprom_write8b(addr_brake_force, this->mem_brake_force); + }; + + /* + No wrong value + if(this->mem_max_hbrake_time > 1000) + { + this->mem_max_hbrake_time = def_max_hbrake_time; + mcu::eeprom_write16b(addr_lock_current, this->mem_max_hbrake_time); + }; + */ + + if(this->mem_lock_current > max_lock_current) { this->mem_lock_current = def_lock_current; mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); @@ -125,10 +151,22 @@ void logic::CfgMemory::save_all(void) mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); }; + if(this->brake_force != this->mem_brake_force) + { + this->mem_brake_force = this->brake_force; + mcu::eeprom_write8b(addr_brake_force, this->mem_brake_force); + }; + + if(this->max_hbrake_time != this->mem_max_hbrake_time) + { + this->mem_max_hbrake_time = this->max_hbrake_time; + mcu::eeprom_write16b(addr_max_hbrake_time, this->mem_max_hbrake_time); + }; + if(this->lock_current != this->mem_lock_current) { this->mem_lock_current = this->lock_current; - mcu::eeprom_write8b(addr_lock_current, this->mem_lock_current); + mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); }; } @@ -139,7 +177,9 @@ void logic::CfgMemory::restore(void) this->pot_mode = this->mem_pot_mode; this->dsp_brigth = this->mem_dsp_brigth; this->dsp_dimm = this->mem_dsp_dimm; - this->lock_current = this->mem_lock_current; + this->brake_force = this->mem_brake_force; + this->max_hbrake_time = this->mem_max_hbrake_time; + this->lock_current = this->mem_lock_current; } /**** Private function definitions ****/ diff --git a/firmware/src/logic/cfg_mem.h b/firmware/src/logic/cfg_mem.h index 45b513e..a7d2f1d 100644 --- a/firmware/src/logic/cfg_mem.h +++ b/firmware/src/logic/cfg_mem.h @@ -17,6 +17,8 @@ class CfgMemory uint8_t mem_pot_mode; uint8_t mem_dsp_brigth; uint8_t mem_dsp_dimm; + uint8_t mem_brake_force; + uint16_t mem_max_hbrake_time; uint16_t mem_lock_current; public: @@ -28,6 +30,8 @@ class CfgMemory uint8_t pot_mode; uint8_t dsp_brigth; uint8_t dsp_dimm; + uint8_t brake_force; + uint16_t max_hbrake_time; uint16_t lock_current; void init(void); -- 2.49.1 From e4bd6d2e04dbf9a2f3defe2f35f6a66e87027d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 12:29:03 +0300 Subject: [PATCH 27/35] New feature update --- firmware/src/hw/devices.cpp | 2 ++ firmware/src/main.cpp | 19 +++++-------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/firmware/src/hw/devices.cpp b/firmware/src/hw/devices.cpp index 650df83..6fd6933 100644 --- a/firmware/src/hw/devices.cpp +++ b/firmware/src/hw/devices.cpp @@ -58,6 +58,8 @@ void devices_update_inputs(void) sup_fuse.update(); out_fuse.update(); + + display.process_timer(); } /**** Private function definitions ****/ diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 842f2ca..bf0ffa7 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,6 +1,5 @@ /**** Includes ****/ #include "utils/utils.h" -#include "utils/interpolate.h" #include "hw/devices.h" @@ -14,9 +13,6 @@ static const uint16_t dsp_lock_bmode = 1000; static const uint16_t dsp_lock_force = 50; -static const uint16_t cfg_max_hbrake_time = 1000; -static const uint8_t cfg_brake_force = 100; - /**** Private variables ****/ static logic::CfgMemory cfg_mem = logic::CfgMemory(); @@ -34,12 +30,11 @@ int main(void) cfg_mem.init(); uint8_t user_force = 0; - uint16_t dsp_lock = 0; button_force.force = cfg_mem.btn_force; dccd_force.brake_mode = cfg_mem.bmode; - dccd_force.max_hbrake_time = cfg_max_hbrake_time; - dccd_force.brake_force = cfg_brake_force; + dccd_force.max_hbrake_time = cfg_mem.max_hbrake_time; + dccd_force.brake_force = cfg_mem.brake_force; // Super loop while(1) @@ -85,20 +80,16 @@ int main(void) break; } display.write(bmode_img); - dsp_lock = dsp_lock_bmode; + display.set_lock(dsp_lock_bmode); dccd_force.is_new_bmode = 0; } else if((button_force.is_new)&&(cfg_mem.pot_mode==0)) { display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); - dsp_lock = dsp_lock_force; + display.set_lock(dsp_lock_force); button_force.is_new = 0; } - else - { - if(dsp_lock) dsp_lock--; - else display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); - } + else if(display.locked==0) display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); // Process dimm if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(cfg_mem.dsp_dimm); -- 2.49.1 From 91a7247bee4bf4b0b2ac4f26e59bf71b4d3b7866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 14:20:45 +0300 Subject: [PATCH 28/35] Created regulated output --- firmware/src/hw/devices.cpp | 8 ++-- firmware/src/hw/devices.h | 5 +-- firmware/src/hw/reg_out.cpp | 71 ++++++++++++++++++++++++++++++++++ firmware/src/hw/reg_out.h | 45 +++++++++++++++++++++ firmware/src/logic/cfg_mem.cpp | 40 ++++++++++++++++++- firmware/src/logic/cfg_mem.h | 4 ++ firmware/src/main.cpp | 19 ++++++++- firmware/src/uDCCD.cppproj | 18 +++------ 8 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 firmware/src/hw/reg_out.cpp create mode 100644 firmware/src/hw/reg_out.h diff --git a/firmware/src/hw/devices.cpp b/firmware/src/hw/devices.cpp index 6fd6933..a1a105b 100644 --- a/firmware/src/hw/devices.cpp +++ b/firmware/src/hw/devices.cpp @@ -5,7 +5,8 @@ /**** Private definitions ****/ /**** Private constants ****/ static const uint16_t def_button_hold_time = 1000; -static const uint16_t def_min_current = 100; +static const uint16_t def_max_voltage = 7000; +static const uint16_t def_min_voltage = 100; static const uint16_t def_fuse_treshold = 6000; static const uint16_t def_fuse_hold_cycles = 50; static const uint16_t def_fuse_cooldown_cycles = 1000; @@ -20,8 +21,9 @@ void devices_init(void) btn_up.hold_time = def_button_hold_time; btn_down.hold_time = def_button_hold_time; - ccout.target = 0; - ccout.min_out = def_min_current; + ccout.max_voltage = def_max_voltage; + ccout.max_current = 0; + ccout.min_voltage = def_min_voltage; sup_fuse.hold_current = def_fuse_treshold; sup_fuse.trip_cycles = def_fuse_hold_cycles; diff --git a/firmware/src/hw/devices.h b/firmware/src/hw/devices.h index 76d480a..01395f3 100644 --- a/firmware/src/hw/devices.h +++ b/firmware/src/hw/devices.h @@ -9,8 +9,7 @@ #include "button.h" #include "potentiometer.h" #include "display_led.h" -#include "cv_output.h" -#include "cc_output.h" +#include "reg_out.h" #include "fuse.h" static hw::Button btn_mode = hw::Button(&din1, bsp::DIN_LOW, 10, hw::BUTTON_OFF); @@ -25,7 +24,7 @@ static hw::Potentiometer pot = hw::Potentiometer(&ain2, 500, 4500); static hw::DisplayLed display = hw::DisplayLed(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); -static hw::CCoutput ccout = hw::CCoutput(&hbridge, &bat_u, &dccd_u, &dccd_i); +static hw::RegOut ccout = hw::RegOut(&hbridge, &bat_u, &dccd_u, &dccd_i); static hw::Fuse sup_fuse = hw::Fuse(&bat_i); static hw::Fuse out_fuse = hw::Fuse(&dccd_i); diff --git a/firmware/src/hw/reg_out.cpp b/firmware/src/hw/reg_out.cpp new file mode 100644 index 0000000..9edc744 --- /dev/null +++ b/firmware/src/hw/reg_out.cpp @@ -0,0 +1,71 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "reg_out.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::RegOut::RegOut(bsp::Hafbridge* hbridge, bsp::AnalogIn* sup_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i) +{ + this->hbridge = hbridge; + this->supply_voltage = sup_u; + this->out_voltage = out_u; + this->out_currnet = out_i; + + this->max_voltage = 6750; + this->max_current = 4500; + this->min_voltage = 200; + + this->set_voltage = 0; + this->out_impedance = 0xFFFF; + + this->hbridge->disable(); +} + +hw::RegOut::~RegOut(void) +{ + this->hbridge->write((uint16_t)0x0000); + this->hbridge->disable(); + return; +} + +void hw::RegOut::update(void) +{ + // Calculate output impedance + if((this->out_currnet == 0)||(this->out_voltage->last_read == 0)) this->out_impedance = 0xFFFF; + else this->out_impedance = util::sat_div_kilo(this->out_voltage->last_read, this->out_currnet->last_read); + + // Recalculate output set voltage + if((this->max_voltage==0)||(this->max_current==0)) this->set_voltage = 0; + else this->set_voltage = util::sat_mul_kilo(this->max_current, this->out_impedance); + + // Limit set voltage + if(this->set_voltage > this->max_voltage) this->set_voltage = this->max_voltage; + + if((this->set_voltage>0)&&(this->set_voltage < this->min_voltage)) this->set_voltage = this->min_voltage; + + // Set output + this->hbridge->write(util::sat_ratio(this->set_voltage, this->supply_voltage->last_read)); +} + +void hw::RegOut::enable(void) +{ + this->hbridge->enable(); +} + +void hw::RegOut::disable(void) +{ + this->hbridge->disable(); +} + +uint8_t hw::RegOut::is_enabled(void) +{ + return this->hbridge->is_enabled(); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/reg_out.h b/firmware/src/hw/reg_out.h new file mode 100644 index 0000000..f2356c7 --- /dev/null +++ b/firmware/src/hw/reg_out.h @@ -0,0 +1,45 @@ +#ifndef REGULATED_OUTPUT_H_ +#define REGULATED_OUTPUT_H_ + +/**** Includes ****/ +#include +#include "../bsp/ain.h" +#include "../bsp/halfbridge.h" + +namespace hw { + +/**** Public definitions ****/ + +class RegOut +{ + protected: + bsp::Hafbridge* hbridge; + bsp::AnalogIn* supply_voltage; + bsp::AnalogIn* out_voltage; + bsp::AnalogIn* out_currnet; + + public: + RegOut(bsp::Hafbridge* hbridge, bsp::AnalogIn* sup_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i); + ~RegOut(void); + + uint16_t max_voltage; + uint16_t max_current; + uint16_t min_voltage; + + uint16_t set_voltage; + uint16_t out_impedance; + + void update(void); + void enable(void); + void disable(void); + uint8_t is_enabled(void); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* REGULATED_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp index 553e7de..0ac10e5 100644 --- a/firmware/src/logic/cfg_mem.cpp +++ b/firmware/src/logic/cfg_mem.cpp @@ -14,6 +14,8 @@ static const uint16_t addr_dsp_dimm = 0x0004; static const uint16_t addr_brake_force = 0x0005; static const uint16_t addr_max_hbrake_time = 0x0006; static const uint16_t addr_lock_current = 0x0008; +static const uint16_t addr_max_out_voltage = 0x000A; +static const uint16_t addr_min_out_voltage = 0x000C; static const uint8_t def_btn_force = 0; static const uint8_t def_pot_mode = 0; @@ -23,8 +25,12 @@ static const uint8_t def_brigth = 100; static const uint8_t def_brake_force = 100; static const uint16_t def_max_hbrake_time = 1000; static const uint16_t def_lock_current = 4500; +static const uint16_t def_max_out_voltage = 7000; +static const uint16_t def_min_out_voltage = 200; -static const uint16_t max_lock_current = 6000; +static const uint16_t lim_saved_lock_current = 6000; +static const uint16_t lim_saved_max_voltage = 8000; +static const uint16_t lim_saved_min_voltage = 1000; /**** Private variables ****/ /**** Private function declarations ****/ @@ -39,6 +45,8 @@ logic::CfgMemory::CfgMemory(void) this->mem_brake_force = 0; this->mem_max_hbrake_time = 0; this->mem_lock_current = 0; + this->mem_max_out_voltage = 0; + this->mem_min_out_voltage = 0; this->restore(); } @@ -58,6 +66,8 @@ void logic::CfgMemory::init(void) this->mem_brake_force = mcu::eeprom_read8b(addr_brake_force); this->mem_max_hbrake_time = mcu::eeprom_read16b(addr_max_hbrake_time); this->mem_lock_current = mcu::eeprom_read16b(addr_lock_current); + this->mem_max_out_voltage = mcu::eeprom_read16b(addr_max_out_voltage); + this->mem_min_out_voltage = mcu::eeprom_read16b(addr_min_out_voltage); // Validate EEPROM data if(this->mem_btn_force > 100) @@ -105,12 +115,24 @@ void logic::CfgMemory::init(void) }; */ - if(this->mem_lock_current > max_lock_current) + if(this->mem_lock_current > lim_saved_lock_current) { this->mem_lock_current = def_lock_current; mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); }; + if(this->mem_max_out_voltage > lim_saved_max_voltage) + { + this->mem_max_out_voltage = def_max_out_voltage; + mcu::eeprom_write16b(addr_max_out_voltage, this->mem_max_out_voltage); + }; + + if(this->mem_min_out_voltage > lim_saved_min_voltage) + { + this->mem_min_out_voltage = def_min_out_voltage; + mcu::eeprom_write16b(addr_min_out_voltage, this->mem_min_out_voltage); + }; + this->restore(); } @@ -168,6 +190,18 @@ void logic::CfgMemory::save_all(void) this->mem_lock_current = this->lock_current; mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); }; + + if(this->max_out_voltage != this->mem_max_out_voltage) + { + this->mem_max_out_voltage = this->max_out_voltage; + mcu::eeprom_write16b(addr_max_out_voltage, this->mem_max_out_voltage); + }; + + if(this->min_out_voltage != this->mem_min_out_voltage) + { + this->mem_min_out_voltage = this->min_out_voltage; + mcu::eeprom_write16b(addr_min_out_voltage, this->mem_min_out_voltage); + }; } void logic::CfgMemory::restore(void) @@ -180,6 +214,8 @@ void logic::CfgMemory::restore(void) this->brake_force = this->mem_brake_force; this->max_hbrake_time = this->mem_max_hbrake_time; this->lock_current = this->mem_lock_current; + this->max_out_voltage = this->mem_max_out_voltage; + this->min_out_voltage = this->mem_min_out_voltage; } /**** Private function definitions ****/ diff --git a/firmware/src/logic/cfg_mem.h b/firmware/src/logic/cfg_mem.h index a7d2f1d..522f757 100644 --- a/firmware/src/logic/cfg_mem.h +++ b/firmware/src/logic/cfg_mem.h @@ -20,6 +20,8 @@ class CfgMemory uint8_t mem_brake_force; uint16_t mem_max_hbrake_time; uint16_t mem_lock_current; + uint16_t mem_max_out_voltage; + uint16_t mem_min_out_voltage; public: CfgMemory(void); @@ -33,6 +35,8 @@ class CfgMemory uint8_t brake_force; uint16_t max_hbrake_time; uint16_t lock_current; + uint16_t max_out_voltage; + uint16_t min_out_voltage; void init(void); void save(void); diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index bf0ffa7..a5f1edf 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -29,8 +29,25 @@ int main(void) // Read saved config cfg_mem.init(); + #ifdef OVERRIDE_CONFIG + cfg_mem.btn_force = 0; + cfg_mem.bmode = 0; + cfg_mem.pot_mode = 0; + cfg_mem.dsp_brigth = 100; + cfg_mem.dsp_dimm = 50; + cfg_mem.brake_force = 100; + cfg_mem.max_hbrake_time = 1000; + cfg_mem.lock_current = 4200; + cfg_mem.max_out_voltage = 6500; + cfg_mem.min_out_voltage = 500; + cfg_mem.save_all(); + #endif + uint8_t user_force = 0; + ccout.max_voltage = cfg_mem.max_out_voltage; + ccout.min_voltage = cfg_mem.min_out_voltage; + button_force.force = cfg_mem.btn_force; dccd_force.brake_mode = cfg_mem.bmode; dccd_force.max_hbrake_time = cfg_mem.max_hbrake_time; @@ -56,7 +73,7 @@ int main(void) if((sup_fuse.fault)||(out_fuse.fault)) dccd_force.force = 0; // Convert force to current - ccout.target = util::percent_of(dccd_force.force, cfg_mem.lock_current); + ccout.max_current = util::percent_of(dccd_force.force, cfg_mem.lock_current); // Execute outputs ccout.update(); diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index a4b7be9..04782f9 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -207,18 +207,6 @@ compile - - compile - - - compile - - - compile - - - compile - compile @@ -243,6 +231,12 @@ compile + + compile + + + compile + compile -- 2.49.1 From 3045b88413d8ab5080c752821052bb82caaaccb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 14:44:52 +0300 Subject: [PATCH 29/35] Improved config memory --- firmware/src/logic/cfg_mem.cpp | 82 +++++++++++----------------------- firmware/src/logic/cfg_mem.h | 4 +- firmware/src/main.cpp | 27 +++++------ 3 files changed, 44 insertions(+), 69 deletions(-) diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp index 0ac10e5..eea5229 100644 --- a/firmware/src/logic/cfg_mem.cpp +++ b/firmware/src/logic/cfg_mem.cpp @@ -16,6 +16,7 @@ static const uint16_t addr_max_hbrake_time = 0x0006; static const uint16_t addr_lock_current = 0x0008; static const uint16_t addr_max_out_voltage = 0x000A; static const uint16_t addr_min_out_voltage = 0x000C; +static const uint16_t addr_cfg_good = 0x000D; static const uint8_t def_btn_force = 0; static const uint8_t def_pot_mode = 0; @@ -28,15 +29,13 @@ static const uint16_t def_lock_current = 4500; static const uint16_t def_max_out_voltage = 7000; static const uint16_t def_min_out_voltage = 200; -static const uint16_t lim_saved_lock_current = 6000; -static const uint16_t lim_saved_max_voltage = 8000; -static const uint16_t lim_saved_min_voltage = 1000; - /**** Private variables ****/ /**** Private function declarations ****/ /**** Public function definitions ****/ logic::CfgMemory::CfgMemory(void) { + this->cfg_good = 0; + this->mem_btn_force = 0; this->mem_bmode = 0; this->mem_pot_mode = 0; @@ -58,6 +57,7 @@ logic::CfgMemory::~CfgMemory(void) void logic::CfgMemory::init(void) { + uint8_t cfg_good_magic = mcu::eeprom_read8b(addr_cfg_good); this->mem_btn_force = mcu::eeprom_read8b(addr_btn_force); this->mem_bmode = mcu::eeprom_read8b(addr_bmode); this->mem_pot_mode = mcu::eeprom_read8b(addr_pot_mode); @@ -70,68 +70,22 @@ void logic::CfgMemory::init(void) this->mem_min_out_voltage = mcu::eeprom_read16b(addr_min_out_voltage); // Validate EEPROM data - if(this->mem_btn_force > 100) + if(cfg_good_magic == 0x37) this->cfg_good = 1; + else this->cfg_good = 0; + + if(this->cfg_good != 1) { this->mem_btn_force = def_btn_force; - mcu::eeprom_write8b(addr_btn_force, this->mem_btn_force); - }; - - if(this->mem_bmode > 2) - { this->mem_bmode = def_bmode; - mcu::eeprom_write8b(addr_bmode, this->mem_bmode); - }; - - if(this->mem_pot_mode > 1) - { this->mem_pot_mode = def_pot_mode; - mcu::eeprom_write8b(addr_pot_mode, this->mem_pot_mode); - }; - - if(this->mem_dsp_brigth > 100) - { this->mem_dsp_brigth = def_brigth; - mcu::eeprom_write8b(addr_dsp_brigth, this->mem_dsp_brigth); - }; - - if(this->mem_dsp_dimm > 100) - { this->mem_dsp_dimm = def_dimm; - mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); - }; - - if(this->mem_brake_force > 100) - { this->mem_brake_force = def_brake_force; - mcu::eeprom_write8b(addr_brake_force, this->mem_brake_force); - }; - - /* - No wrong value - if(this->mem_max_hbrake_time > 1000) - { this->mem_max_hbrake_time = def_max_hbrake_time; - mcu::eeprom_write16b(addr_lock_current, this->mem_max_hbrake_time); - }; - */ - - if(this->mem_lock_current > lim_saved_lock_current) - { this->mem_lock_current = def_lock_current; - mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); - }; - - if(this->mem_max_out_voltage > lim_saved_max_voltage) - { this->mem_max_out_voltage = def_max_out_voltage; - mcu::eeprom_write16b(addr_max_out_voltage, this->mem_max_out_voltage); - }; - - if(this->mem_min_out_voltage > lim_saved_min_voltage) - { this->mem_min_out_voltage = def_min_out_voltage; - mcu::eeprom_write16b(addr_min_out_voltage, this->mem_min_out_voltage); - }; + } this->restore(); } @@ -218,4 +172,22 @@ void logic::CfgMemory::restore(void) this->min_out_voltage = this->mem_min_out_voltage; } +uint8_t logic::CfgMemory::checksum(void) +{ + uint32_t cs = 0; + cs += (uint32_t)this->mem_btn_force; + cs += (uint32_t)this->mem_bmode; + cs += (uint32_t)this->mem_pot_mode; + cs += (uint32_t)this->mem_dsp_brigth; + cs += (uint32_t)this->mem_dsp_dimm; + cs += (uint32_t)this->mem_brake_force; + cs += (uint32_t)this->mem_max_hbrake_time; + cs += (uint32_t)this->mem_lock_current; + cs += (uint32_t)this->mem_max_out_voltage; + cs += (uint32_t)this->mem_min_out_voltage; + + return (uint8_t)cs; +} + + /**** Private function definitions ****/ diff --git a/firmware/src/logic/cfg_mem.h b/firmware/src/logic/cfg_mem.h index 522f757..97460a4 100644 --- a/firmware/src/logic/cfg_mem.h +++ b/firmware/src/logic/cfg_mem.h @@ -11,7 +11,7 @@ namespace logic { class CfgMemory { - protected: + protected: uint8_t mem_btn_force; uint8_t mem_bmode; uint8_t mem_pot_mode; @@ -27,6 +27,7 @@ class CfgMemory CfgMemory(void); ~CfgMemory(void); + uint8_t cfg_good; uint8_t btn_force; uint8_t bmode; uint8_t pot_mode; @@ -42,6 +43,7 @@ class CfgMemory void save(void); void save_all(void); void restore(void); + uint8_t checksum(void); }; /**** Public function declarations ****/ diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index a5f1edf..a29bbfe 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -29,19 +29,20 @@ int main(void) // Read saved config cfg_mem.init(); - #ifdef OVERRIDE_CONFIG - cfg_mem.btn_force = 0; - cfg_mem.bmode = 0; - cfg_mem.pot_mode = 0; - cfg_mem.dsp_brigth = 100; - cfg_mem.dsp_dimm = 50; - cfg_mem.brake_force = 100; - cfg_mem.max_hbrake_time = 1000; - cfg_mem.lock_current = 4200; - cfg_mem.max_out_voltage = 6500; - cfg_mem.min_out_voltage = 500; - cfg_mem.save_all(); - #endif + if(cfg_mem.cfg_good !=1 ) + { + cfg_mem.btn_force = 0; + cfg_mem.bmode = 0; + cfg_mem.pot_mode = 0; + cfg_mem.dsp_brigth = 100; + cfg_mem.dsp_dimm = 50; + cfg_mem.brake_force = 100; + cfg_mem.max_hbrake_time = 1000; + cfg_mem.lock_current = 4200; + cfg_mem.max_out_voltage = 6500; + cfg_mem.min_out_voltage = 500; + cfg_mem.save_all(); + }; uint8_t user_force = 0; -- 2.49.1 From 2ea17201e9f7b26bacb126d82c7f191eb3fb6083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Sat, 13 Apr 2024 19:10:46 +0300 Subject: [PATCH 30/35] Fixed MCU setup --- firmware/src/bsp/board.cpp | 9 ++++++--- firmware/src/main.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/firmware/src/bsp/board.cpp b/firmware/src/bsp/board.cpp index 83216bc..e93755d 100644 --- a/firmware/src/bsp/board.cpp +++ b/firmware/src/bsp/board.cpp @@ -11,10 +11,13 @@ void board_init(void) { // MCU setup + // ADC clock must be 50kHz to 200kHz + // ADC clock = 8MHz/ADC_DIV + // PWM frequncy = 8MHz/(2*TOP*TIM_DIM) mcu::startupCfg_t mcu_cfg; - mcu_cfg.adc_clk = mcu::ADC_DIV2; - mcu_cfg.pwm_clk = mcu::TIM_DIV1; - mcu_cfg.pwm_top = 200; + mcu_cfg.adc_clk = mcu::ADC_DIV64; //125kHz /13.5 = 9259 samples/s + mcu_cfg.pwm_clk = mcu::TIM_DIV1; // 8MHz + mcu_cfg.pwm_top = 1000; // 4kHz mcu_cfg.pwm_ch1_en = 1; mcu::startup(&mcu_cfg); diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index a29bbfe..96aa6c5 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -11,7 +11,7 @@ /**** Private definitions ****/ /**** Private constants ****/ static const uint16_t dsp_lock_bmode = 1000; -static const uint16_t dsp_lock_force = 50; +static const uint16_t dsp_lock_force = 500; /**** Private variables ****/ static logic::CfgMemory cfg_mem = logic::CfgMemory(); -- 2.49.1 From 8c47b9cae803179f0b1292f4f6d39958cb394432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 5 Jun 2024 17:34:17 +0300 Subject: [PATCH 31/35] Started another rework --- firmware/src/bsp/mcu/mcu_hal.h | 69 +++++++++++++----------- firmware/src/uDCCD.cppproj | 98 ---------------------------------- 2 files changed, 39 insertions(+), 128 deletions(-) diff --git a/firmware/src/bsp/mcu/mcu_hal.h b/firmware/src/bsp/mcu/mcu_hal.h index 83c664c..01f9e14 100644 --- a/firmware/src/bsp/mcu/mcu_hal.h +++ b/firmware/src/bsp/mcu/mcu_hal.h @@ -10,41 +10,43 @@ namespace mcu { /* */ -const uint8_t GPIO0 = 0; //PC5 Mode -const uint8_t GPIO1 = 1; //PC4 Pot -const uint8_t GPIO2 = 2; //PE1 Down -const uint8_t GPIO3 = 3; //PE3 Up -const uint8_t GPIO4 = 4; //PD7 Dimm -const uint8_t GPIO5 = 5; //PB7 Brakes -const uint8_t GPIO6 = 6; //PB6 Handbrake -const uint8_t GPIO7 = 7; //PB5 Handbrake pull -const uint8_t GPIO8 = 8; //PD6 Speed pull -const uint8_t GPIO9 = 9; //PD0 LED0 -const uint8_t GPIO10 = 10; //PD1 LED1 -const uint8_t GPIO11 = 11; //PD2 LED2 -const uint8_t GPIO12 = 12; //PD3 LED3 -const uint8_t GPIO13 = 13; //PD4 LED4 -const uint8_t GPIO14 = 14; //PD5 LED5 -const uint8_t GPIO15 = 15; //PB0 DCCD Enable -const uint8_t GPIO16 = 16; //PB1 DCCD PWM -const uint8_t GPIO17 = 17; //PB2 LED PWM +const uint8_t GPIO_DIN1 = 0; +const uint8_t GPIO_DIN2 = 1; +const uint8_t GPIO_DIN3 = 2; +const uint8_t GPIO_DIN4 = 3; +const uint8_t GPIO_HVDIN1 = 4; +const uint8_t GPIO_HVDIN2 = 5; +const uint8_t GPIO_HVDIN3 = 6; +const uint8_t GPIO_HVDIN3_PULL = 7; +const uint8_t GPIO_OD1 = 8; +const uint8_t GPIO_OD2 = 9; +const uint8_t GPIO_OD3 = 10; +const uint8_t GPIO_OD4 = 11; +const uint8_t GPIO_OD5 = 12; +const uint8_t GPIO_OD6 = 13; +const uint8_t GPIO_OUT_LOW = 14; +const uint8_t GPIO_OUT_HIGH = 15; +const uint8_t GPIO_OD_PWM = 16; +const uint8_t GPIO_FREQ_PULL = 17; +const uint8_t GPIO_FREQ1 = 18; +const uint8_t GPIO_FREQ2 = 19; const uint8_t LEVEL_LOW = 0; const uint8_t LEVEL_HIGH = 1; -const int8_t LEVEL_HIZ = -1; +const int8_t LEVEL_HIZ = -1; -const uint8_t ADC0 = 0; //Output current -const uint8_t ADC1 = 1; //Output voltage -const uint8_t ADC2 = 2; //Battery voltage -const uint8_t ADC3 = 3; //Battery current -const uint8_t ADC4 = 4; //Potentiometer -const uint8_t ADC5 = 5; //Mode -const uint8_t ADC8 = 8; //MCU temperature -const uint8_t ADC14 = 14; //MCU internal reference -const uint8_t ADC15 = 15; //MCU ground +const uint8_t ADC_IOUT = 0; //Output current +const uint8_t ADC_VOUT = 1; //Output voltage +const uint8_t ADC_VBAT = 2; //Battery voltage +const uint8_t ADC_IBAT = 3; //Battery current +const uint8_t ADC_AIN2 = 4; //Potentiometer +const uint8_t ADC_AIN1 = 5; //Mode +const uint8_t ADC_TEMP = 8; //MCU temperature +const uint8_t ADC_IVREF = 14; //MCU internal reference +const uint8_t ADC_GND = 15; //MCU ground -const uint8_t PWM0 = 0; //DCCD -const uint8_t PWM1 = 1; //LED +const uint8_t PWM_OUT = 0; //DCCD +const uint8_t PWM_OD = 1; //LED //ADC definitions typedef enum { @@ -85,6 +87,13 @@ uint16_t adc_read(uint8_t ch); void pwm_write(uint8_t ch, uint16_t dc); uint16_t pwm_read(uint8_t ch); +void timer_reset(uint8_t ch); +uint16_t timer_read(uint8_t ch); +uint16_t timer_read_top(uint8_t ch); + +uint32_t timer_convert_us(uint8_t ch, uint16_t raw); +uint32_t timer_convert_ms(uint8_t ch, uint16_t raw); + uint8_t eeprom_read8b(uint16_t address); uint16_t eeprom_read16b(uint16_t address); uint32_t eeprom_read32b(uint16_t address); diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 04782f9..f730fb9 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -153,108 +153,12 @@ - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - compile compile - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - - - compile - compile @@ -274,8 +178,6 @@ - - -- 2.49.1 From 6219290880476c5d74dac801962fabdfc4aed9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Thu, 25 Jul 2024 18:46:17 +0300 Subject: [PATCH 32/35] Continued work --- firmware/src/bsp/ain.cpp | 14 +- firmware/src/bsp/ain.h | 15 +- firmware/src/bsp/ain_lpf.cpp | 52 +++ firmware/src/bsp/ain_lpf.h | 36 ++ firmware/src/bsp/board.cpp | 124 ++++-- firmware/src/bsp/board.h | 88 +++-- firmware/src/bsp/din.cpp | 32 +- firmware/src/bsp/din.h | 25 +- firmware/src/bsp/dio.cpp | 34 -- firmware/src/bsp/dio.h | 32 -- firmware/src/bsp/dout.cpp | 39 +- firmware/src/bsp/dout.h | 30 +- firmware/src/bsp/halfbridge.cpp | 74 ---- firmware/src/bsp/halfbridge.h | 38 -- firmware/src/bsp/mcu/mcu_hal.h | 23 +- firmware/src/bsp/mcu/mcu_hal_r8.cpp | 575 +++++++++++++++------------- firmware/src/bsp/pwm.cpp | 40 -- firmware/src/bsp/pwm.h | 31 -- firmware/src/bsp/pwm_out.cpp | 56 +++ firmware/src/bsp/pwm_out.h | 37 ++ firmware/src/hw/button.cpp | 112 +++--- firmware/src/hw/button.h | 43 ++- firmware/src/hw/cc_output.cpp | 41 -- firmware/src/hw/cc_output.h | 35 -- firmware/src/hw/cv_output.cpp | 53 --- firmware/src/hw/cv_output.h | 39 -- firmware/src/hw/dccd_hw.cpp | 110 ++++++ firmware/src/hw/dccd_hw.h | 64 ++++ firmware/src/hw/devices.cpp | 67 ---- firmware/src/hw/devices.h | 34 -- firmware/src/hw/display_led.cpp | 195 ---------- firmware/src/hw/display_led.h | 54 --- firmware/src/hw/fuse.cpp | 66 ---- firmware/src/hw/fuse.h | 40 -- firmware/src/hw/led_display.cpp | 157 ++++++++ firmware/src/hw/led_display.h | 66 ++++ firmware/src/hw/out_driver.cpp | 105 +++++ firmware/src/hw/out_driver.h | 45 +++ firmware/src/hw/potentiometer.cpp | 31 +- firmware/src/hw/potentiometer.h | 21 +- firmware/src/hw/reg_out.cpp | 71 ---- firmware/src/hw/reg_out.h | 45 --- firmware/src/logic/button_force.cpp | 51 --- firmware/src/logic/button_force.h | 36 -- firmware/src/logic/cfg_mem.cpp | 193 ---------- firmware/src/logic/cfg_mem.h | 56 --- firmware/src/logic/dccd_force.cpp | 84 ---- firmware/src/logic/dccd_force.h | 42 -- firmware/src/main.cpp | 110 +----- firmware/src/sort/cv_out.cpp | 50 +++ firmware/src/sort/cv_out.h | 41 ++ firmware/src/sort/fuse.cpp | 81 ++++ firmware/src/sort/fuse.h | 48 +++ firmware/src/sort/voltlock.cpp | 83 ++++ firmware/src/sort/voltlock.h | 49 +++ firmware/src/uDCCD.cppproj | 73 ++++ firmware/src/utils/utils.cpp | 52 +++ firmware/src/utils/utils.h | 34 +- firmware/src/utils/vcounter.cpp | 67 ++++ firmware/src/utils/vcounter.h | 42 ++ 60 files changed, 2024 insertions(+), 2057 deletions(-) create mode 100644 firmware/src/bsp/ain_lpf.cpp create mode 100644 firmware/src/bsp/ain_lpf.h delete mode 100644 firmware/src/bsp/dio.cpp delete mode 100644 firmware/src/bsp/dio.h delete mode 100644 firmware/src/bsp/halfbridge.cpp delete mode 100644 firmware/src/bsp/halfbridge.h delete mode 100644 firmware/src/bsp/pwm.cpp delete mode 100644 firmware/src/bsp/pwm.h create mode 100644 firmware/src/bsp/pwm_out.cpp create mode 100644 firmware/src/bsp/pwm_out.h delete mode 100644 firmware/src/hw/cc_output.cpp delete mode 100644 firmware/src/hw/cc_output.h delete mode 100644 firmware/src/hw/cv_output.cpp delete mode 100644 firmware/src/hw/cv_output.h create mode 100644 firmware/src/hw/dccd_hw.cpp create mode 100644 firmware/src/hw/dccd_hw.h delete mode 100644 firmware/src/hw/devices.cpp delete mode 100644 firmware/src/hw/devices.h delete mode 100644 firmware/src/hw/display_led.cpp delete mode 100644 firmware/src/hw/display_led.h delete mode 100644 firmware/src/hw/fuse.cpp delete mode 100644 firmware/src/hw/fuse.h create mode 100644 firmware/src/hw/led_display.cpp create mode 100644 firmware/src/hw/led_display.h create mode 100644 firmware/src/hw/out_driver.cpp create mode 100644 firmware/src/hw/out_driver.h delete mode 100644 firmware/src/hw/reg_out.cpp delete mode 100644 firmware/src/hw/reg_out.h delete mode 100644 firmware/src/logic/button_force.cpp delete mode 100644 firmware/src/logic/button_force.h delete mode 100644 firmware/src/logic/cfg_mem.cpp delete mode 100644 firmware/src/logic/cfg_mem.h delete mode 100644 firmware/src/logic/dccd_force.cpp delete mode 100644 firmware/src/logic/dccd_force.h create mode 100644 firmware/src/sort/cv_out.cpp create mode 100644 firmware/src/sort/cv_out.h create mode 100644 firmware/src/sort/fuse.cpp create mode 100644 firmware/src/sort/fuse.h create mode 100644 firmware/src/sort/voltlock.cpp create mode 100644 firmware/src/sort/voltlock.h create mode 100644 firmware/src/utils/vcounter.cpp create mode 100644 firmware/src/utils/vcounter.h diff --git a/firmware/src/bsp/ain.cpp b/firmware/src/bsp/ain.cpp index 1d4cc06..bfaac3b 100644 --- a/firmware/src/bsp/ain.cpp +++ b/firmware/src/bsp/ain.cpp @@ -9,9 +9,18 @@ using namespace bsp; /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ -bsp::AnalogIn::AnalogIn(uint8_t adc_ch) +bsp::AnalogIn::AnalogIn(void) +{ + return; +} + +bsp::AnalogIn::~AnalogIn(void) +{ + return; +} + +void bsp::AnalogIn::init(uint8_t adc_ch) { this->adc_ch = adc_ch; this->mul = DEF_AIN_MUL; @@ -20,6 +29,7 @@ bsp::AnalogIn::AnalogIn(uint8_t adc_ch) this->last_read = 0; } + uint16_t bsp::AnalogIn::read(void) { //Read ADC diff --git a/firmware/src/bsp/ain.h b/firmware/src/bsp/ain.h index 00b90b5..75e8eb0 100644 --- a/firmware/src/bsp/ain.h +++ b/firmware/src/bsp/ain.h @@ -12,12 +12,12 @@ static const uint8_t DEF_AIN_DIV = 44; static const int16_t DEF_AIN_OFFSET = 0; class AnalogIn -{ - protected: - uint8_t adc_ch; - +{ public: - AnalogIn(uint8_t adc_ch); + AnalogIn(void); + ~AnalogIn(void); + + void init(uint8_t adc_ch); uint8_t mul; uint8_t div; @@ -25,6 +25,11 @@ class AnalogIn uint16_t last_read; uint16_t read(void); + + #ifndef TESTING + protected: + #endif + uint8_t adc_ch; }; /**** Public function declarations ****/ diff --git a/firmware/src/bsp/ain_lpf.cpp b/firmware/src/bsp/ain_lpf.cpp new file mode 100644 index 0000000..a24f491 --- /dev/null +++ b/firmware/src/bsp/ain_lpf.cpp @@ -0,0 +1,52 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "ain_lpf.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +bsp::AnalogInLfp::AnalogInLfp(void) +{ + return; +} + +bsp::AnalogInLfp::~AnalogInLfp(void) +{ + return; +} + +void bsp::AnalogInLfp::init(uint8_t adc_ch) +{ + this->adc_ch = adc_ch; + this->mul = DEF_AIN_MUL; + this->div = DEF_AIN_DIV; + this->offset = DEF_AIN_OFFSET; + this->strength = 0; + this->last_read = 0; + this->last_read_direct = 0; +} + +uint16_t bsp::AnalogInLfp::read(void) +{ + //Read ADC + uint16_t raw = mcu::adc_read(this->adc_ch); + + //Convert to mV + this->last_read_direct = util::convert_muldivoff(raw, this->mul, this->div, this->offset); + + // Do filtering + uint32_t td0 = ((uint32_t)(255 - this->strength) * this->last_read_direct); + uint32_t td1 = ((uint32_t)(this->strength) * this->last_read); + uint32_t out = (td0 + td1)/255; + + this->last_read = util::sat_cast(out); + + return this->last_read; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/bsp/ain_lpf.h b/firmware/src/bsp/ain_lpf.h new file mode 100644 index 0000000..6a6609f --- /dev/null +++ b/firmware/src/bsp/ain_lpf.h @@ -0,0 +1,36 @@ +#ifndef ANALOG_IN_LPF_H_ +#define ANALOG_IN_LPF_H_ + +/**** Includes ****/ +#include +#include "ain.h" + +namespace bsp { + +/**** Public definitions ****/ +class AnalogInLfp : public AnalogIn +{ + public: + // New stuff + AnalogInLfp(void); + ~AnalogInLfp(void); + + void init(uint8_t adc_ch); + uint16_t read(void); + + uint8_t strength; + uint16_t last_read_direct; + + #ifndef TESTING + protected: + #endif +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* ANALOG_IN_LPF_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/board.cpp b/firmware/src/bsp/board.cpp index e93755d..6cff7da 100644 --- a/firmware/src/bsp/board.cpp +++ b/firmware/src/bsp/board.cpp @@ -1,66 +1,106 @@ /**** Includes ****/ #include "../utils/utils.h" +#include "mcu/mcu_hal.h" #include "board.h" +using namespace bsp; + /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ -void board_init(void) +bsp::Board::Board(void) { - // MCU setup - // ADC clock must be 50kHz to 200kHz - // ADC clock = 8MHz/ADC_DIV - // PWM frequncy = 8MHz/(2*TOP*TIM_DIM) + return; +} + +bsp::Board::~Board(void) +{ + return; +} + +void bsp::Board::init(void) +{ + // Controller setup mcu::startupCfg_t mcu_cfg; - mcu_cfg.adc_clk = mcu::ADC_DIV64; //125kHz /13.5 = 9259 samples/s - mcu_cfg.pwm_clk = mcu::TIM_DIV1; // 8MHz - mcu_cfg.pwm_top = 1000; // 4kHz - mcu_cfg.pwm_ch1_en = 1; + + mcu_cfg.adc_clk = mcu::ADC_DIV64; // 8MHz/64=125kHz + mcu_cfg.pwm_clk = mcu::TIM_DIV1; // 8MHz/1 = 8MHz + mcu_cfg.pwm_top = 500; // 8000kHz/500 = 16kHz + mcu_cfg.od_common_is_pwm = 1; // Open-drain common is PWM mcu::startup(&mcu_cfg); - // Board setup - // Fixed function AIN mV and mA scale - dccd_i.mul = 215; - dccd_i.div = 22; - dccd_i.offset = 0; - dccd_i.last_read = 0; + // Analog inputs + this->out_voltage.init(mcu::ADC_VOUT); + this->out_voltage.mul = 20; + this->out_voltage.div = 1; + this->out_voltage.offset = 0; - dccd_u.mul = 20; - dccd_u.div = 1; - dccd_u.offset = 0; - dccd_u.last_read = 0; + this->out_current.init(mcu::ADC_IOUT); + this->out_current.mul = 215; + this->out_current.div = 22; + this->out_current.offset = 0; - bat_u.mul = 20; - bat_u.div = 1; - bat_u.offset = 0; - bat_u.last_read = 12000; + this->battery_voltage.init(mcu::ADC_VBAT); + this->battery_voltage.mul = 20; + this->battery_voltage.div = 1; + this->battery_voltage.offset = 0; - bat_i.mul = 235; - bat_i.div = 6; - bat_i.offset = 0; - bat_i.last_read = 0; + this->battery_current.init(mcu::ADC_IBAT); + this->battery_current.mul = 235; + this->battery_current.div = 6; + this->battery_current.offset = 0; + + this->ain1.init(mcu::ADC_AIN1); + this->ain2.init(mcu::ADC_AIN2); + + // Digital inputs + this->din1.init(mcu::GPIO_DIN1, 0); + this->din2.init(mcu::GPIO_DIN2, 0); + this->din3.init(mcu::GPIO_DIN3, 0); + this->din4.init(mcu::GPIO_DIN4, 0); + this->hvdin1.init(mcu::GPIO_HVDIN1, 1); + this->hvdin2.init(mcu::GPIO_HVDIN2, 1); + this->hvdin3.init(mcu::GPIO_HVDIN3, 1); + + this->hvdin3_pull.init(mcu::GPIO_HVDIN3_PULL, 0); + this->freq_pull.init(mcu::GPIO_FREQ_PULL, 0); + + // Open-drain outputs + this->od1.init(mcu::GPIO_OD1, 1); + this->od2.init(mcu::GPIO_OD2, 1); + this->od3.init(mcu::GPIO_OD3, 1); + this->od4.init(mcu::GPIO_OD4, 1); + this->od5.init(mcu::GPIO_OD5, 1); + this->od6.init(mcu::GPIO_OD6, 1); + this->od_pwm.init(mcu::PWM_OD, 100); + + // PWM driver output + this->out_pwm.init(mcu::PWM_OUT, 95); + this->out_low.init(mcu::GPIO_OUT_LOW, 0); } -void board_read(void) +void bsp::Board::read(void) { - dccd_i.read(); - dccd_u.read(); - bat_u.read(); - bat_i.read(); - ain1.read(); - ain2.read(); + // Update all analog inputs + this->out_voltage.read(); + this->out_current.read(); + this->battery_voltage.read(); + this->battery_current.read(); + this->ain1.read(); + this->ain2.read(); - din1.read(); - din2.read(); - din3.read(); - din4.read(); - hvdin1.read(); - hvdin2.read(); - hvdin3.read(); + // Update all digital inputs + this->din1.read(); + this->din2.read(); + this->din3.read(); + this->din4.read(); + this->hvdin1.read(); + this->hvdin2.read(); + this->hvdin3.read(); } /**** Private function definitions ****/ + diff --git a/firmware/src/bsp/board.h b/firmware/src/bsp/board.h index ab108f9..665109c 100644 --- a/firmware/src/bsp/board.h +++ b/firmware/src/bsp/board.h @@ -1,41 +1,67 @@ -#ifndef BSP_BOARD_H_ -#define BSP_BOARD_H_ +#ifndef UDCCD_BOARD_H_ +#define UDCCD_BOARD_H_ /**** Includes ****/ #include -#include "mcu/mcu_hal.h" #include "ain.h" +#include "ain_lpf.h" #include "din.h" #include "dout.h" -#include "dio.h" -#include "halfbridge.h" -#include "pwm.h" +#include "pwm_out.h" -static bsp::AnalogIn dccd_i = bsp::AnalogIn(mcu::ADC0); -static bsp::AnalogIn dccd_u = bsp::AnalogIn(mcu::ADC1); -static bsp::AnalogIn bat_u = bsp::AnalogIn(mcu::ADC2); -static bsp::AnalogIn bat_i = bsp::AnalogIn(mcu::ADC3); -static bsp::Hafbridge hbridge = bsp::Hafbridge(mcu::PWM0, mcu::GPIO15, 95); -static bsp::AnalogIn ain1 = bsp::AnalogIn(mcu::ADC5); // mode -static bsp::AnalogIn ain2 = bsp::AnalogIn(mcu::ADC4); // pot -static bsp::DigitalIn din1 = bsp::DigitalIn(mcu::GPIO0, 0, bsp::DIN_HIGH); //mode -static bsp::DigitalIn din2 = bsp::DigitalIn(mcu::GPIO1, 0, bsp::DIN_HIGH); //pot -static bsp::DigitalIn din3 = bsp::DigitalIn(mcu::GPIO2, 0, bsp::DIN_HIGH); //down -static bsp::DigitalIn din4 = bsp::DigitalIn(mcu::GPIO3, 0, bsp::DIN_HIGH); //up -static bsp::DigitalIn hvdin1 = bsp::DigitalIn(mcu::GPIO4, 1, bsp::DIN_LOW); //dimm -static bsp::DigitalIn hvdin2 = bsp::DigitalIn(mcu::GPIO5, 1, bsp::DIN_LOW); //brakes -static bsp::DigitalIn hvdin3 = bsp::DigitalIn(mcu::GPIO6, 1, bsp::DIN_LOW); //hbrake -static bsp::DigitalIO hvdin3_pull = bsp::DigitalIO(mcu::GPIO7, bsp::DIN_HIGH); //hbrake pull -static bsp::DigitalOut odout1 = bsp::DigitalOut(mcu::GPIO9, 1); -static bsp::DigitalOut odout2 = bsp::DigitalOut(mcu::GPIO10, 1); -static bsp::DigitalOut odout3 = bsp::DigitalOut(mcu::GPIO11, 1); -static bsp::DigitalOut odout4 = bsp::DigitalOut(mcu::GPIO12, 1); -static bsp::DigitalOut odout5 = bsp::DigitalOut(mcu::GPIO13, 1); -static bsp::DigitalOut odout6 = bsp::DigitalOut(mcu::GPIO14, 1); -static bsp::PWMout od_pwm = bsp::PWMout(mcu::PWM1); +namespace bsp { -void board_init(void); -void board_read(void); +/**** Public definitions ****/ +class Board +{ + public: + Board(void); + ~Board(void); + + void init(void); + + AnalogIn out_voltage; + AnalogIn out_current; + AnalogIn battery_voltage; + AnalogIn battery_current; + AnalogIn ain1; + AnalogIn ain2; + + DigitalIn din1; + DigitalIn din2; + DigitalIn din3; + DigitalIn din4; + DigitalIn hvdin1; + DigitalIn hvdin2; + DigitalIn hvdin3; + + DigitalOut hvdin3_pull; + DigitalOut freq_pull; + + DigitalOut od1; + DigitalOut od2; + DigitalOut od3; + DigitalOut od4; + DigitalOut od5; + DigitalOut od6; + PwmOut od_pwm; + + PwmOut out_pwm; + DigitalOut out_low; + + void read(void); -#endif /* BSP_BOARD_H_ */ \ No newline at end of file + #ifndef TESTING + protected: + #endif +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* UDCCD_BOARD_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/din.cpp b/firmware/src/bsp/din.cpp index 4ea82d3..dd9b953 100644 --- a/firmware/src/bsp/din.cpp +++ b/firmware/src/bsp/din.cpp @@ -9,15 +9,10 @@ using namespace bsp; /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ -bsp::DigitalIn::DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value) +bsp::DigitalIn::DigitalIn(void) { - this->gpio_ch = gpio_ch; - this->invert = inverted; - - if(init_value) this->last_read = DIN_HIGH; - else this->last_read = DIN_LOW; + return; } bsp::DigitalIn::~DigitalIn(void) @@ -25,16 +20,29 @@ bsp::DigitalIn::~DigitalIn(void) return; } +void bsp::DigitalIn::init(uint8_t gpio_ch, uint8_t inverted) +{ + this->gpio_ch = gpio_ch; + if(inverted == 0) this->is_inverted = 0; + else this->is_inverted = 1; + this->last_read = 0; +} + + uint8_t bsp::DigitalIn::read(void) { - uint8_t lvl = mcu::gpio_read(this->gpio_ch); + // Read ADC + this->last_read = mcu::gpio_read(this->gpio_ch); - if(this->invert) lvl = util::invert(lvl); - - if(lvl>0) this->last_read = DIN_HIGH; - else this->last_read = DIN_LOW; + // Invert if necessary + if(this->is_inverted) + { + if(this->last_read==0) this->last_read = 1; + else this->last_read = 0; + }; return this->last_read; } /**** Private function definitions ****/ + diff --git a/firmware/src/bsp/din.h b/firmware/src/bsp/din.h index 8d8e924..af0080d 100644 --- a/firmware/src/bsp/din.h +++ b/firmware/src/bsp/din.h @@ -1,5 +1,5 @@ -#ifndef DIGITAL_INPUT_H_ -#define DIGITAL_INPUT_H_ +#ifndef DIGITAL_IN_H_ +#define DIGITAL_IN_H_ /**** Includes ****/ #include @@ -7,22 +7,23 @@ namespace bsp { /**** Public definitions ****/ -const uint8_t DIN_LOW = 0; -const uint8_t DIN_HIGH = 1; - class DigitalIn -{ - protected: - uint8_t gpio_ch; - uint8_t invert; - +{ public: - DigitalIn(uint8_t gpio_ch, uint8_t inverted, uint8_t init_value); + DigitalIn(void); ~DigitalIn(void); + void init(uint8_t gpio_ch, uint8_t inverted); + uint8_t last_read; uint8_t read(void); + + #ifndef TESTING + protected: + #endif + uint8_t gpio_ch; + uint8_t is_inverted; }; /**** Public function declarations ****/ @@ -32,4 +33,4 @@ class DigitalIn } //namespace -#endif /* DIGITAL_INPUT_H_ */ \ No newline at end of file +#endif /* DIGITAL_IN_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/dio.cpp b/firmware/src/bsp/dio.cpp deleted file mode 100644 index ead2112..0000000 --- a/firmware/src/bsp/dio.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "dio.h" - -using namespace bsp; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -bsp::DigitalIO::DigitalIO(uint8_t gpio_ch, uint8_t init_value) : DigitalIn(gpio_ch, 0, init_value), DigitalOut(gpio_ch, 0) -{ - return; -} - -bsp::DigitalIO::~DigitalIO(void) -{ - this->write(DOUT_HIZ); -} - -uint8_t bsp::DigitalIO::is_io_match(void) -{ - if(this->last_set == DOUT_HIZ) return 1; - - uint8_t read_lvl = this->read(); - - if(read_lvl == (uint8_t)this->last_set) return 1; - else return 0; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/bsp/dio.h b/firmware/src/bsp/dio.h deleted file mode 100644 index 0506654..0000000 --- a/firmware/src/bsp/dio.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef DIGITAL_IO_H_ -#define DIGITAL_IO_H_ - -/**** Includes ****/ -#include -#include "din.h" -#include "dout.h" - -namespace bsp { - -/**** Public definitions ****/ -const int8_t DIO_LOW = 0; -const int8_t DIO_HIGH = 1; -const int8_t DIO_HIZ = -1; - -class DigitalIO : public DigitalIn, public DigitalOut -{ - public: - DigitalIO(uint8_t gpio_ch, uint8_t init_value); - ~DigitalIO(void); - - uint8_t is_io_match(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* DIGITAL_IO_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/dout.cpp b/firmware/src/bsp/dout.cpp index fdb880b..cdb7f07 100644 --- a/firmware/src/bsp/dout.cpp +++ b/firmware/src/bsp/dout.cpp @@ -9,44 +9,29 @@ using namespace bsp; /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ -bsp::DigitalOut::DigitalOut(uint8_t gpio_ch, uint8_t inverted) +bsp::DigitalOut::DigitalOut(void) { - this->gpio_ch = gpio_ch; - this->invert = inverted; - this->write(DOUT_HIZ); + return; } bsp::DigitalOut::~DigitalOut(void) { - this->write(DOUT_HIZ); + return; } void bsp::DigitalOut::write(int8_t level) { - if(level > 0) + if(this->is_inverted) { - this->last_set = DOUT_HIGH; - if(this->invert) mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); - else mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); - } - else if(level == 0) - { - this->last_set = DOUT_LOW; - if(this->invert) mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); - else mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); - } - else - { - this->last_set = DOUT_HIZ; - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIZ); - } -} - -int8_t bsp::DigitalOut::get_set_level(void) -{ - return this->last_set; + if(level==0) level = 1; + else if (level > 0) level = 0; + }; + + mcu::gpio_write(this->gpio_ch, level); + + this->last_writen = level; } /**** Private function definitions ****/ + diff --git a/firmware/src/bsp/dout.h b/firmware/src/bsp/dout.h index 49c696e..36e8d98 100644 --- a/firmware/src/bsp/dout.h +++ b/firmware/src/bsp/dout.h @@ -1,29 +1,27 @@ -#ifndef DIGITAL_OUTPUT_H_ -#define DIGITAL_OUTPUT_H_ +#ifndef DIGITAL_OUT_H_ +#define DIGITAL_OUT_H_ /**** Includes ****/ #include +#include "din.h" namespace bsp { /**** Public definitions ****/ -const int8_t DOUT_LOW = 0; -const int8_t DOUT_HIGH = 1; -const int8_t DOUT_HIZ = -1; - -class DigitalOut -{ - protected: - uint8_t gpio_ch; - uint8_t invert; - int8_t last_set; - +class DigitalOut : public DigitalIn +{ public: - DigitalOut(uint8_t gpio_ch, uint8_t inverted); + // New or redefined stuff + DigitalOut(void); ~DigitalOut(void); + int8_t last_writen; + void write(int8_t level); - int8_t get_set_level(void); + + #ifndef TESTING + protected: + #endif }; /**** Public function declarations ****/ @@ -33,4 +31,4 @@ class DigitalOut } //namespace -#endif /* DIGITAL_OUTPUT_H_ */ \ No newline at end of file +#endif /* DIGITAL_OUT_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/halfbridge.cpp b/firmware/src/bsp/halfbridge.cpp deleted file mode 100644 index 23321be..0000000 --- a/firmware/src/bsp/halfbridge.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "halfbridge.h" - -using namespace bsp; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -bsp::Hafbridge::Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc) -{ - this->pwm_ch = hs_pwm_ch; - this->gpio_ch = ls_gpio_ch; - - if(max_dc>100) max_dc = 100; - - this->max_dc = util::percent_to_16b(max_dc); - - this->disable(); -} - -bsp::Hafbridge::~Hafbridge(void) -{ - this->last_duty = 0; - this->disable(); -} - -void bsp::Hafbridge::write(uint16_t dividend) -{ - // Limit duty - if(dividend > this->max_dc) dividend = this->max_dc; - this->last_duty = dividend; - - if(this->enabled == 0) return; - - // Set PWM - mcu::pwm_write(this->pwm_ch, dividend); -} - -void bsp::Hafbridge::write(uint8_t percent) -{ - // Convert to dividend/0xFFFF - this->write(util::percent_to_16b(percent)); -} - -void bsp::Hafbridge::enable(void) -{ - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_HIGH); - this->enabled = 1; - this->write(this->last_duty); -} - -void bsp::Hafbridge::disable(void) -{ - mcu::pwm_write(this->pwm_ch, 0); - mcu::gpio_write(this->gpio_ch, mcu::LEVEL_LOW); - this->enabled = 0; -} - -uint8_t bsp::Hafbridge::get_set_duty(void) -{ - return this->last_duty; -} - -uint8_t bsp::Hafbridge::is_enabled(void) -{ - return this->enabled; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/bsp/halfbridge.h b/firmware/src/bsp/halfbridge.h deleted file mode 100644 index c75a21e..0000000 --- a/firmware/src/bsp/halfbridge.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef HALFBRIDGE_H_ -#define HALFBRIDGE_H_ - -/**** Includes ****/ -#include - -namespace bsp { - -/**** Public definitions ****/ -class Hafbridge -{ - protected: - uint8_t pwm_ch; - uint8_t gpio_ch; - uint16_t last_duty; - uint8_t enabled; - uint16_t max_dc; - - public: - Hafbridge(uint8_t hs_pwm_ch, uint8_t ls_gpio_ch, uint8_t max_dc); - ~Hafbridge(void); - - void write(uint16_t dividend); - void write(uint8_t percent); - void enable(void); - void disable(void); - uint8_t get_set_duty(void); - uint8_t is_enabled(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* HALFBRIDGE_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/mcu/mcu_hal.h b/firmware/src/bsp/mcu/mcu_hal.h index 01f9e14..187d17d 100644 --- a/firmware/src/bsp/mcu/mcu_hal.h +++ b/firmware/src/bsp/mcu/mcu_hal.h @@ -9,6 +9,9 @@ namespace mcu { /**** Public definitions ****/ /* */ +const uint8_t LEVEL_LOW = 0; +const uint8_t LEVEL_HIGH = 1; +const int8_t LEVEL_HIZ = -1; const uint8_t GPIO_DIN1 = 0; const uint8_t GPIO_DIN2 = 1; @@ -27,13 +30,11 @@ const uint8_t GPIO_OD6 = 13; const uint8_t GPIO_OUT_LOW = 14; const uint8_t GPIO_OUT_HIGH = 15; const uint8_t GPIO_OD_PWM = 16; -const uint8_t GPIO_FREQ_PULL = 17; -const uint8_t GPIO_FREQ1 = 18; -const uint8_t GPIO_FREQ2 = 19; - -const uint8_t LEVEL_LOW = 0; -const uint8_t LEVEL_HIGH = 1; -const int8_t LEVEL_HIZ = -1; +const uint8_t GPIO_FREQ1 = 17; +const uint8_t GPIO_FREQ2 = 18; +const uint8_t GPIO_FREQ_PULL = 19; +const uint8_t GPIO_TX = 20; +const uint8_t GPIO_RX = 21; const uint8_t ADC_IOUT = 0; //Output current const uint8_t ADC_VOUT = 1; //Output voltage @@ -72,16 +73,22 @@ typedef struct { adcClkDiv_t adc_clk; timerClkDiv_t pwm_clk; uint16_t pwm_top; - uint8_t pwm_ch1_en; + uint8_t od_common_is_pwm; } startupCfg_t; /**** Public function declarations ****/ void startup(startupCfg_t* hwCfg); +void rtc_set_calibration(uint16_t coef); + uint8_t gpio_read(uint8_t ch); void gpio_write(uint8_t ch, int8_t lvl); void gpio_write_pull(uint8_t ch, int8_t lvl); +void adc_start(uint8_t ch); +uint8_t adc_is_running(void); +uint8_t adc_is_new(void); +uint16_t adc_read(void); uint16_t adc_read(uint8_t ch); void pwm_write(uint8_t ch, uint16_t dc); diff --git a/firmware/src/bsp/mcu/mcu_hal_r8.cpp b/firmware/src/bsp/mcu/mcu_hal_r8.cpp index 6b2d1de..2056d15 100644 --- a/firmware/src/bsp/mcu/mcu_hal_r8.cpp +++ b/firmware/src/bsp/mcu/mcu_hal_r8.cpp @@ -8,6 +8,8 @@ using namespace mcu; /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ +static volatile uint16_t rtc_ms = 1000; + /**** Private function declarations ****/ static uint8_t gpio_read_level(uint8_t pin_reg, uint8_t mask); static void pwm_write_ocx(uint8_t ch, uint16_t value); @@ -34,9 +36,9 @@ void mcu::startup(startupCfg_t* hwCfg) DDRB |= 0x03; //Set as output // Common OD PWM pin - if(hwCfg->pwm_ch1_en) PORTB &= ~0x04; //Set low - else PORTB |= 0x04; //Set high - DDRB |= 0x04; //Set as output + if(hwCfg->od_common_is_pwm) PORTB &= ~0x04; //Set low + else PORTB |= 0x04; //Set high + DDRB |= 0x04; //Set as output // OD control pins PORTD &= ~0x3F; //Set low (off) @@ -66,6 +68,10 @@ void mcu::startup(startupCfg_t* hwCfg) PORTC &= ~0x30; //Pull-up off DDRC &= ~0x30; //Set as inputs + // Freq-pull control pins + PORTD &= ~0x40; //Set low + DDRD |= 0x40; //Set as output + //ADC configuration PRR0 &= ~0x01; //Enable ADC power DIDR0 |= 0x0F; //Disable digital inputs, ADC0-ADC3 @@ -80,7 +86,7 @@ void mcu::startup(startupCfg_t* hwCfg) //DCCD and LED PWM configuration PRR0 &= ~0x80; //Enable Timer1 power TCCR1A = 0xC2; //Connect OC1A, inverted mode - if(hwCfg->pwm_ch1_en) TCCR1A |= 0x30; //Connect OC1B, inverted mode + if(hwCfg->od_common_is_pwm) TCCR1A |= 0x30; //Connect OC1B, inverted mode TCCR1B = 0x18; //PWM, Phase & Frequency Correct ICR1 top, no clock, WGM:0xE TCCR1C = 0x00; TCNT1 = 0x0000; @@ -94,7 +100,309 @@ void mcu::startup(startupCfg_t* hwCfg) TCCR1B |= tim1_prescaler; //Enable timer } -// ADC Interface functions +void mcu::rtc_set_calibration(uint16_t coef) +{ + rtc_ms = coef; +} + +// GPIO interface functions +uint8_t mcu::gpio_read(uint8_t ch) +{ + switch(ch) + { + case GPIO_DIN1: // Mode DIN1 + return gpio_read_level(PINC,0x20); + + case GPIO_DIN2: // Pot DIN2 + return gpio_read_level(PINC,0x10); + + case GPIO_DIN3: // Down DIN3 + return gpio_read_level(PINE,0x02); + + case GPIO_DIN4: // Up DIN4 + return gpio_read_level(PINE,0x08); + + case GPIO_HVDIN1: // Dimm DIN5 + return gpio_read_level(PIND,0x80); + + case GPIO_HVDIN2: // Brakes DIN6 + return gpio_read_level(PINB,0x80); + + case GPIO_HVDIN3: // Handbrake DIN7 + return gpio_read_level(PINB,0x40); + + case GPIO_HVDIN3_PULL: // Handbrake pull DIN8 + return gpio_read_level(PINB,0x20); + + case GPIO_OD1: // LED 0 + return gpio_read_level(PIND,0x01); + + case GPIO_OD2: // LED 1 + return gpio_read_level(PIND,0x02); + + case GPIO_OD3: // LED 2 + return gpio_read_level(PIND,0x04); + + case GPIO_OD4: // LED 3 + return gpio_read_level(PIND,0x08); + + case GPIO_OD5: // LED 4 + return gpio_read_level(PIND,0x10); + + case GPIO_OD6: // LED 5 + return gpio_read_level(PIND,0x20); + + case GPIO_OUT_LOW: // DCCD Enable + return gpio_read_level(PINB,0x01); + + case GPIO_OUT_HIGH: // DCCD PWM + return gpio_read_level(PINB,0x02); + + case GPIO_OD_PWM: // LED PWM + return gpio_read_level(PINB,0x04); + + case GPIO_FREQ1: // Speed 1 + return gpio_read_level(PINE,0x04); + + case GPIO_FREQ2: // Speed 2 + return gpio_read_level(PINE,0x01); + + case GPIO_FREQ_PULL: // Speed-pull + return gpio_read_level(PIND,0x40); + + case GPIO_TX: // + return gpio_read_level(PINB,0x08); + + case GPIO_RX: // + return gpio_read_level(PINB,0x10); + + default: + return 0; + } +} + +void mcu::gpio_write(uint8_t ch, int8_t lvl) +{ + switch(ch) + { + case GPIO_DIN1: // Mode DIN1 + if(lvl>0) + { + PORTC |= 0x20; + DDRC |= 0x20; + } + else if(lvl<0) + { + DDRC &= ~0x20; + PORTC &= ~0x20; + } + else + { + PORTC &= ~0x20; + DDRC |= 0x20; + } + return; + + case GPIO_DIN2: // Pot DIN2 + if(lvl>0) + { + PORTC |= 0x10; + DDRC |= 0x10; + } + else if(lvl<0) + { + DDRC &= ~0x10; + PORTC &= ~0x10; + } + else + { + PORTC &= ~0x10; + DDRC |= 0x10; + } + return; + + case GPIO_DIN3: // Down DIN3 + if(lvl>0) + { + PORTE |= 0x02; + DDRE |= 0x02; + } + else if(lvl<0) + { + DDRE &= ~0x02; + PORTE &= ~0x02; + } + else + { + PORTE &= ~0x02; + DDRE |= 0x02; + } + return; + + case GPIO_DIN4: // Up DIN4 + if(lvl>0) + { + PORTE |= 0x08; + DDRE |= 0x08; + } + else if(lvl<0) + { + DDRE &= ~0x08; + PORTE &= ~0x08; + } + else + { + PORTE &= ~0x08; + DDRE |= 0x08; + } + return; + + case GPIO_HVDIN3_PULL: // Handbrake pull DIN + if(lvl>0) + { + PORTB |= 0x20; + DDRB |= 0x20; + } + else if(lvl<0) + { + DDRB &= ~0x20; + PORTB &= ~0x20; + } + else + { + PORTB &= ~0x20; + DDRB |= 0x20; + } + return; + + case GPIO_OD1: // LED 0 + if(lvl>0) PORTD |= 0x01; + else PORTD &= ~0x01; + return; + + case GPIO_OD2: // LED 1 + if(lvl>0) PORTD |= 0x02; + else PORTD &= ~0x02; + return; + + case GPIO_OD3: // LED 2 + if(lvl>0) PORTD |= 0x04; + else PORTD &= ~0x04; + return; + + case GPIO_OD4: // LED 3 + if(lvl>0) PORTD |= 0x08; + else PORTD &= ~0x08; + return; + + case GPIO_OD5: // LED 4 + if(lvl>0) PORTD |= 0x10; + else PORTD &= ~0x10; + return; + + case GPIO_OD6: // LED 5 + if(lvl>0) PORTD |= 0x20; + else PORTD &= ~0x20; + return; + + case GPIO_OUT_LOW: // DCCD Enable + if(lvl>0) PORTB |= 0x01; + else PORTB &= ~0x01; + return; + + case GPIO_FREQ_PULL: // Speed-pull + if(lvl>0) PORTD |= 0x40; + else PORTD &= ~0x40; + return; + + default: + return; + } +} + +void mcu::gpio_write_pull(uint8_t ch, int8_t lvl) +{ + switch(ch) + { + case GPIO_DIN1: // Mode DIN1 + if(lvl>0) PORTC |= 0x20; + else PORTC &= ~0x20; + return; + + case GPIO_DIN2: // Pot DIN2 + if(lvl>0) PORTC |= 0x10; + else PORTC &= ~0x10; + return; + + case GPIO_DIN3: // Down DIN3 + if(lvl>0) PORTE |= 0x02; + else PORTE &= ~0x02; + return; + + case GPIO_DIN4: // Up DIN4 + if(lvl>0) PORTE |= 0x08; + else PORTE &= ~0x08; + return; + + case GPIO_HVDIN1: // Dimm + if(lvl>0) PORTD |= 0x80; + else PORTD &= ~0x80; + return; + + case GPIO_HVDIN2: // Brakes + if(lvl>0) PORTB |= 0x80; + else PORTB &= ~0x80; + return; + + case GPIO_HVDIN3: // Handbrake + if(lvl>0) PORTB |= 0x40; + else PORTB &= ~0x40; + return; + + default: + return; + } +} + +// ADC interface functions +void mcu::adc_start(uint8_t ch) +{ + // check if already running + if(ADCSRA&0x40) return; + + //check if ADC is enabled + if(!(ADCSRA&0x80)) return; + + //Safe guard mux + if(ch > 15) return; + // Not available channels + if((ch > 8) && (ch<14)) return; + + ADMUX &= ~0x0F; + ADMUX |= ch; + ADCSRA |= 0x10; // Reset int. flag + ADCSRA |= 0x40; +} + + +uint8_t mcu::adc_is_running(void) +{ + if(ADCSRA&0x40) return 1; + else return 0; +} + +uint8_t mcu::adc_is_new(void) +{ + if(ADCSRA&0x10) return 1; + else return 0; +} + +uint16_t mcu::adc_read(void) +{ + ADCSRA |= 0x10; // Reset int. flag + return ADC; +} + uint16_t mcu::adc_read(uint8_t ch) { //check if ADC is enabled @@ -112,7 +420,7 @@ uint16_t mcu::adc_read(uint8_t ch) return ADC; } -// PWM Timer Interface functions +// PWM interface functions void mcu::pwm_write(uint8_t ch, uint16_t dc) { dc = 0xFFFF - dc; @@ -148,252 +456,7 @@ uint16_t mcu::pwm_read(uint8_t ch) return (uint16_t)temp; } -uint8_t mcu::gpio_read(uint8_t ch) -{ - switch(ch) - { - case GPIO0: // Mode DIN1 - return gpio_read_level(PINC,0x20); - - case GPIO1: // Pot DIN2 - return gpio_read_level(PINC,0x10); - - case GPIO2: // Down DIN3 - return gpio_read_level(PINE,0x02); - - case GPIO3: // Up DIN4 - return gpio_read_level(PINE,0x08); - - case GPIO4: // Dimm DIN5 - return gpio_read_level(PIND,0x80); - - case GPIO5: // Brakes DIN6 - return gpio_read_level(PINB,0x80); - - case GPIO6: // Handbrake DIN7 - return gpio_read_level(PINB,0x40); - - case GPIO7: // Handbrake pull DIN8 - return gpio_read_level(PINB,0x20); - - case GPIO8: // Speed-pull - return gpio_read_level(PIND,0x40); - - case GPIO9: // LED 0 - return gpio_read_level(PIND,0x01); - - case GPIO10: // LED 1 - return gpio_read_level(PIND,0x02); - - case GPIO11: // LED 2 - return gpio_read_level(PIND,0x04); - - case GPIO12: // LED 3 - return gpio_read_level(PIND,0x08); - - case GPIO13: // LED 4 - return gpio_read_level(PIND,0x10); - - case GPIO14: // LED 5 - return gpio_read_level(PIND,0x20); - - case GPIO15: // DCCD Enable - return gpio_read_level(PINB,0x01); - - case GPIO16: // DCCD PWM - return gpio_read_level(PINB,0x02); - - case GPIO17: // LED PWM - return gpio_read_level(PINB,0x04); - - default: - return 0; - } -} - -void mcu::gpio_write(uint8_t ch, int8_t lvl) -{ - switch(ch) - { - case GPIO0: // Mode DIN1 - if(lvl>0) - { - PORTC |= 0x20; - DDRC |= 0x20; - } - else if(lvl<0) - { - DDRC &= ~0x20; - PORTC &= ~0x20; - } - else - { - PORTC &= ~0x20; - DDRC |= 0x20; - } - return; - - case GPIO1: // Pot DIN2 - if(lvl>0) - { - PORTC |= 0x10; - DDRC |= 0x10; - } - else if(lvl<0) - { - DDRC &= ~0x10; - PORTC &= ~0x10; - } - else - { - PORTC &= ~0x10; - DDRC |= 0x10; - } - return; - - case GPIO2: // Down DIN3 - if(lvl>0) - { - PORTE |= 0x02; - DDRE |= 0x02; - } - else if(lvl<0) - { - DDRE &= ~0x02; - PORTE &= ~0x02; - } - else - { - PORTE &= ~0x02; - DDRE |= 0x02; - } - return; - - case GPIO3: // Up DIN4 - if(lvl>0) - { - PORTE |= 0x08; - DDRE |= 0x08; - } - else if(lvl<0) - { - DDRE &= ~0x08; - PORTE &= ~0x08; - } - else - { - PORTE &= ~0x08; - DDRE |= 0x08; - } - return; - - case GPIO7: // Handbrake pull DIN - if(lvl>0) - { - PORTB |= 0x20; - DDRB |= 0x20; - } - else if(lvl<0) - { - DDRB &= ~0x20; - PORTB &= ~0x20; - } - else - { - PORTB &= ~0x20; - DDRB |= 0x20; - } - return; - - case GPIO8: // Speed-pull - if(lvl>0) PORTD |= 0x40; - else PORTD &= ~0x40; - return; - - case GPIO9: // LED 0 - if(lvl>0) PORTD |= 0x01; - else PORTD &= ~0x01; - return; - - case GPIO10: // LED 1 - if(lvl>0) PORTD |= 0x02; - else PORTD &= ~0x02; - return; - - case GPIO11: // LED 2 - if(lvl>0) PORTD |= 0x04; - else PORTD &= ~0x04; - return; - - case GPIO12: // LED 3 - if(lvl>0) PORTD |= 0x08; - else PORTD &= ~0x08; - return; - - case GPIO13: // LED 4 - if(lvl>0) PORTD |= 0x10; - else PORTD &= ~0x10; - return; - - case GPIO14: // LED 5 - if(lvl>0) PORTD |= 0x20; - else PORTD &= ~0x20; - return; - - case GPIO15: // DCCD Enable - if(lvl>0) PORTB |= 0x01; - else PORTB &= ~0x01; - return; - - default: - return; - } -} - -void mcu::gpio_write_pull(uint8_t ch, int8_t lvl) -{ - switch(ch) - { - case GPIO0: // Mode DIN1 - if(lvl>0) PORTC |= 0x20; - else PORTC &= ~0x20; - return; - - case GPIO1: // Pot DIN2 - if(lvl>0) PORTC |= 0x10; - else PORTC &= ~0x10; - return; - - case GPIO2: // Down DIN3 - if(lvl>0) PORTE |= 0x02; - else PORTE &= ~0x02; - return; - - case GPIO3: // Up DIN4 - if(lvl>0) PORTE |= 0x08; - else PORTE &= ~0x08; - return; - - case GPIO4: // Dimm - if(lvl>0) PORTD |= 0x80; - else PORTD &= ~0x80; - return; - - case GPIO5: // Brakes - if(lvl>0) PORTB |= 0x80; - else PORTB &= ~0x80; - return; - - case GPIO6: // Handbrake - if(lvl>0) PORTB |= 0x40; - else PORTB &= ~0x40; - return; - - default: - return; - } -} - +// EEPROM interface functions uint8_t mcu::eeprom_read8b(uint16_t address) { return eeprom_read_byte((uint8_t*)address); @@ -435,11 +498,11 @@ static void pwm_write_ocx(uint8_t ch, uint16_t value) { switch(ch) { - case PWM0: + case PWM_OUT: OCR1A = value; return; - case PWM1: + case PWM_OD: OCR1B = value; return; @@ -452,10 +515,10 @@ static uint16_t pwm_read_ocx(uint8_t ch) { switch(ch) { - case PWM0: + case PWM_OUT: return OCR1A; - case PWM1: + case PWM_OD: return OCR1B ; default: diff --git a/firmware/src/bsp/pwm.cpp b/firmware/src/bsp/pwm.cpp deleted file mode 100644 index 32db2c5..0000000 --- a/firmware/src/bsp/pwm.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "mcu/mcu_hal.h" -#include "pwm.h" - -using namespace bsp; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -bsp::PWMout::PWMout(uint8_t pwm_ch) -{ - this->pwm_ch = pwm_ch; - this->write(0); -} - -bsp::PWMout::~PWMout(void) -{ - this->write(0); -} - -void bsp::PWMout::write(uint8_t duty) -{ - // Convert percent to 16b duty cycle - uint16_t dc = util::percent_to_16b(duty); - - // Set PWM - mcu::pwm_write(this->pwm_ch, dc); - this->last_duty = duty; -} - -uint8_t bsp::PWMout::get_set_duty(void) -{ - return this->last_duty; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/bsp/pwm.h b/firmware/src/bsp/pwm.h deleted file mode 100644 index c5b6cef..0000000 --- a/firmware/src/bsp/pwm.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PWM_H_ -#define PWM_H_ - -/**** Includes ****/ -#include - -namespace bsp { - -/**** Public definitions ****/ -class PWMout -{ - protected: - uint8_t pwm_ch; - uint8_t last_duty; - - public: - PWMout(uint8_t pwm_ch); - ~PWMout(void); - - void write(uint8_t duty); - uint8_t get_set_duty(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* PWM_H_ */ \ No newline at end of file diff --git a/firmware/src/bsp/pwm_out.cpp b/firmware/src/bsp/pwm_out.cpp new file mode 100644 index 0000000..72c34ab --- /dev/null +++ b/firmware/src/bsp/pwm_out.cpp @@ -0,0 +1,56 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "mcu/mcu_hal.h" +#include "pwm_out.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +bsp::PwmOut::PwmOut(void) +{ + return; +} + +bsp::PwmOut::~PwmOut(void) +{ + this->last_duty = 0; +} + +void bsp::PwmOut::init(uint8_t pwm_ch, uint8_t max_dc) +{ + this->pwm_ch = pwm_ch; + + this->last_duty = 0; + + if(max_dc>100) max_dc = 100; + + this->max_dc = util::percent_to_16b(max_dc); +} + +void bsp::PwmOut::write(uint16_t numerator) +{ + // Update target + if(numerator > this->max_dc) numerator = this->max_dc; + this->last_duty = numerator; + + // Set PWM + mcu::pwm_write(this->pwm_ch, numerator); +} + +void bsp::PwmOut::write(uint8_t percent) +{ + // Convert to numerator/0xFFFF + this->write(util::percent_to_16b(percent)); +} + +uint16_t bsp::PwmOut::get_set_duty(void) +{ + return this->last_duty; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/bsp/pwm_out.h b/firmware/src/bsp/pwm_out.h new file mode 100644 index 0000000..cdcfec1 --- /dev/null +++ b/firmware/src/bsp/pwm_out.h @@ -0,0 +1,37 @@ +#ifndef PWM_OUT_H_ +#define PWM_OUT_H_ + +/**** Includes ****/ +#include + +namespace bsp { + +/**** Public definitions ****/ +class PwmOut +{ + public: + PwmOut(void); + ~PwmOut(void); + + void init(uint8_t pwm_ch, uint8_t max_dc); + + void write(uint16_t numerator); + void write(uint8_t percent); + uint16_t get_set_duty(void); + + #ifndef TESTING + protected: + #endif + uint8_t pwm_ch; + uint16_t last_duty; + uint16_t max_dc; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* PWM_OUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/button.cpp b/firmware/src/hw/button.cpp index e1153c2..f847af7 100644 --- a/firmware/src/hw/button.cpp +++ b/firmware/src/hw/button.cpp @@ -8,25 +8,10 @@ using namespace hw; /**** Private constants ****/ /**** Private variables ****/ /**** Private function declarations ****/ - /**** Public function definitions ****/ -hw::Button::Button(bsp::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state) +hw::Button::Button(void) { - this->din_ch = din_ch; - - if(act_lvl) this->act_lvl = bsp::DIN_HIGH; - else this->act_lvl = bsp::DIN_LOW; - - this->dbnc_cnter = 0; - this->dbnc_lim = dbnc_lim; - - if(init_state) this->state = BUTTON_ON; - else this->state = BUTTON_OFF; - - this->time = 0; - this->is_new = 0; - - this->hold_time = 0; + return; } hw::Button::~Button(void) @@ -34,48 +19,67 @@ hw::Button::~Button(void) return; } -uint8_t hw::Button::update(void) +void hw::Button::init(bsp::DigitalIn* din_ch, uint8_t act_lvl, util::VCounter* timer, uint16_t dbnc_lim) { - // Read din level + this->din_ch = din_ch; + this->timer = timer; + + if(act_lvl) this->act_lvl = 1; + else this->act_lvl = 0; + + this->state_start_ts = 0; + this->dbnc_ts = 0; + this->dbnc_lim = dbnc_lim; + + this->state = BUTTON_OFF; + this->is_new = 0; +} + +uint8_t hw::Button::process(void) +{ + // Read din + if(this->update_din) this->din_ch->read(); + + // Get last read level uint8_t lvl = this->din_ch->last_read; - // Increase state counter - this->time = util::sat_add(this->time, 1); - - // Repeat new flag after hold time - if((this->state == BUTTON_ON)&&(this->time > this->hold_time)&&(this->hold_time > 0)) - { - this->time = 0; - this->is_new = 1; - }; - // Determine next state uint8_t next_state = BUTTON_OFF; if(lvl==this->act_lvl) next_state = BUTTON_ON; // Advance debounce sample counter - if(next_state != this->state) this->dbnc_cnter++; - else this->dbnc_cnter = 0; - - // Check for debounce end - if(this->dbnc_cnter < this->dbnc_lim) return this->state; - - // Debounce end. Apply new state. - this->state = next_state; - this->time = 0; - this->is_new = 1; - this->dbnc_cnter = 0; + uint16_t ts_now = this->timer->read(); + if(next_state != this->state) + { + if(this->dbnc_ts == 0) this->dbnc_ts = ts_now; + uint16_t td = util::time_delta(this->dbnc_ts, ts_now); + uint32_t td_ms = this->timer->convert_ms(td); + // Check for debounce end + if(td_ms >= this->dbnc_lim) + { + // Debounce end. Apply new state. + this->dbnc_ts = 0; + this->state = next_state; + this->state_start_ts = ts_now; + this->is_new = 1; + }; + } + else this->dbnc_ts = 0; + return this->state; } -uint8_t hw::Button::force_update(void) +uint8_t hw::Button::force_read(void) { - // Read din level - uint8_t lvl = this->din_ch->read(); + // Read din + if(this->update_din) this->din_ch->read(); + + // Get last read level + uint8_t lvl = this->din_ch->last_read; // Cancels active debounce - this->dbnc_cnter = 0; + this->dbnc_ts = 0; // Determine next state uint8_t next_state = BUTTON_OFF; @@ -83,12 +87,30 @@ uint8_t hw::Button::force_update(void) if(next_state != this->state) { + this->state_start_ts = this->timer->read(); this->state = next_state; - this->time = 0; this->is_new = 1; }; return this->state; } +uint32_t hw::Button::time_read(void) +{ + uint16_t ts_now = this->timer->read(); + uint16_t td = util::time_delta(this->state_start_ts, ts_now); + return this->timer->convert_ms(td); +} + +void hw::Button::time_reset(void) +{ + this->state_start_ts = this->timer->read(); +} + +uint32_t hw::Button::time_read_max(void) +{ + uint16_t ts_max = this->timer->read_top(); + return this->timer->convert_ms(ts_max); +} + /**** Private function definitions ****/ diff --git a/firmware/src/hw/button.h b/firmware/src/hw/button.h index e0f59e1..3be0c0a 100644 --- a/firmware/src/hw/button.h +++ b/firmware/src/hw/button.h @@ -1,42 +1,49 @@ -#ifndef BUTTON_H_ -#define BUTTON_H_ +#ifndef BUTTONS_H_ +#define BUTTONS_H_ /**** Includes ****/ #include -#include "../bsp/din.h" +#include "../utils/vcounter.h" +#include "../bsp/board.h" namespace hw { /**** Public definitions ****/ -const uint8_t BUTTON_OFF = 0; -const uint8_t BUTTON_ON = 1; +const uint8_t BUTTON_OFF = 0; +const uint8_t BUTTON_ON = 1; class Button { - protected: - bsp::DigitalIn* din_ch; - uint8_t act_lvl; - uint8_t dbnc_cnter; - public: - Button(bsp::DigitalIn* din_ch, uint8_t act_lvl, uint8_t dbnc_lim, uint8_t init_state); + Button(void); ~Button(void); uint8_t state; - uint16_t time; - uint8_t dbnc_lim; + uint16_t dbnc_lim; uint8_t is_new; - uint16_t hold_time; + uint8_t update_din; - uint8_t update(void); - uint8_t force_update(void); + void init(bsp::DigitalIn* din_ch, uint8_t act_lvl, util::VCounter* timer, uint16_t dbnc_lim); + uint8_t process(void); + uint8_t force_read(void); + uint32_t time_read(void); + void time_reset(void); + uint32_t time_read_max(void); + + #ifndef TESTING + protected: + #endif + bsp::DigitalIn* din_ch; + util::VCounter* timer; + uint8_t act_lvl; + uint16_t state_start_ts; + uint16_t dbnc_ts; }; /**** Public function declarations ****/ - #ifdef TESTING #endif } //namespace -#endif /* BUTTON_H_ */ \ No newline at end of file +#endif /* BUTTONS_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cc_output.cpp b/firmware/src/hw/cc_output.cpp deleted file mode 100644 index bd3e713..0000000 --- a/firmware/src/hw/cc_output.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "cc_output.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::CCoutput::CCoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i) : CVoutput(hbridge, supply_u) -{ - this->out_voltage = out_u; - this->out_currnet = out_i; - - this->out_impedance = 0xFFFF; - this->target_voltage = 0; -} - -void hw::CCoutput::update(void) -{ - // Calculate output impedance - if((this->out_currnet == 0)||(this->out_voltage->last_read == 0)) this->out_impedance = 0xFFFF; - else - { - this->out_impedance = util::sat_div_kilo(this->out_voltage->last_read, this->out_currnet->last_read); - } - - // Check target - if((this->target < this->min_out)&&(this->target > 0)) this->target = this->min_out; - - // Convert target current to voltage - this->target_voltage = util::sat_mul_kilo(this->target, this->out_impedance); - - // Set output - this->hbridge->write(util::sat_ratio(this->target_voltage, this->supply->last_read)); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/cc_output.h b/firmware/src/hw/cc_output.h deleted file mode 100644 index fee738f..0000000 --- a/firmware/src/hw/cc_output.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CONST_CURRENT_OUTPUT_H_ -#define CONST_CURRENT_OUTPUT_H_ - -/**** Includes ****/ -#include -#include "../bsp/ain.h" -#include "cv_output.h" - -namespace hw { - -/**** Public definitions ****/ - -class CCoutput : public CVoutput -{ - protected: - bsp::AnalogIn* out_voltage; - bsp::AnalogIn* out_currnet; - - public: - CCoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i); - - void update(void); - - uint16_t out_impedance; - uint16_t target_voltage; -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/cv_output.cpp b/firmware/src/hw/cv_output.cpp deleted file mode 100644 index 8913c9a..0000000 --- a/firmware/src/hw/cv_output.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "cv_output.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::CVoutput::CVoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u) -{ - this->hbridge = hbridge; - this->supply = supply_u; - this->target = 0; - this->min_out = 0; - this->hbridge->disable(); -} - -hw::CVoutput::~CVoutput(void) -{ - this->hbridge->write((uint16_t)0x0000); - this->hbridge->disable(); - return; -} - -void hw::CVoutput::update(void) -{ - // Check target - if((this->target < this->min_out)&&(this->target > 0)) this->target = this->min_out; - - // Set output - this->hbridge->write(util::sat_ratio(this->target, this->supply->last_read)); -} - -void hw::CVoutput::enable(void) -{ - this->hbridge->enable(); -} - -void hw::CVoutput::disable(void) -{ - this->hbridge->disable(); -} - -uint8_t hw::CVoutput::is_enabled(void) -{ - return this->hbridge->is_enabled(); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/cv_output.h b/firmware/src/hw/cv_output.h deleted file mode 100644 index 03de3a9..0000000 --- a/firmware/src/hw/cv_output.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef CONST_VOLTAGE_OUTPUT_H_ -#define CONST_VOLTAGE_OUTPUT_H_ - -/**** Includes ****/ -#include -#include "../bsp/ain.h" -#include "../bsp/halfbridge.h" - -namespace hw { - -/**** Public definitions ****/ - -class CVoutput -{ - protected: - bsp::Hafbridge* hbridge; - bsp::AnalogIn* supply; - - public: - CVoutput(bsp::Hafbridge* hbridge, bsp::AnalogIn* supply_u); - ~CVoutput(void); - - uint16_t target; - uint16_t min_out; - - void update(void); - void enable(void); - void disable(void); - uint8_t is_enabled(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/dccd_hw.cpp b/firmware/src/hw/dccd_hw.cpp new file mode 100644 index 0000000..fef1c58 --- /dev/null +++ b/firmware/src/hw/dccd_hw.cpp @@ -0,0 +1,110 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "../bsp/mcu/mcu_hal.h" +#include "dccd_hw.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +hw::DccdHw::DccdHw(void) +{ + return; +} + +hw::DccdHw::~DccdHw(void) +{ + return; +} + +void hw::DccdHw::init(dccdHwCfg_t* cfg) +{ + this->board_hw.init(); + + this->counter.init(0xFFFF, 900); + + this->out_voltage = 0; + this->out_current = 0; + this->battery_voltage = 12000; + this->battery_current = 0; + + this->btn_up.init(&(this->board_hw.din4), 0, &(this->counter), 10); + this->btn_up.update_din = 0; + + this->btn_down.init(&(this->board_hw.din3), 0, &(this->counter), 10); + this->btn_down.update_din = 0; + + this->btn_mode.init(&(this->board_hw.din1), 0, &(this->counter), 10); + this->btn_mode.update_din = 0; + + this->handbrake.init(&(this->board_hw.hvdin3), 0, &(this->counter), 10); + this->handbrake.update_din = 0; + + this->brakes.init(&(this->board_hw.hvdin2), 1, &(this->counter), 10); + this->brakes.update_din = 0; + + this->dimm.init(&(this->board_hw.hvdin1), 1, &(this->counter), 10); + this->dimm.update_din = 0; + + this->pot.init(&(this->board_hw.ain2), 500, 4500); + this->pot.update_ain = 0; + + this->outdriver.init(&(this->board_hw.out_pwm), &(this->board_hw.out_low)); + + LedDisplay::doutCfg_t dsp_cfg; + dsp_cfg.led0_dout_ch = &(this->board_hw.od1); + dsp_cfg.led1_dout_ch = &(this->board_hw.od2); + dsp_cfg.led2_dout_ch = &(this->board_hw.od3); + dsp_cfg.led3_dout_ch = &(this->board_hw.od4); + dsp_cfg.led4_dout_ch = &(this->board_hw.od5); + dsp_cfg.led5_dout_ch = &(this->board_hw.od6); + + this->display.init(&dsp_cfg, 0, &(this->counter), &(this->board_hw.od_pwm)); + + // Apply configuration + if(cfg->handbrake_pull_up) + { + this->board_hw.hvdin3_pull.write(1); + } + else this->board_hw.hvdin3_pull.write(0); + + if(cfg->speed_hall) + { + this->board_hw.freq_pull.write(1); + } + else this->board_hw.freq_pull.write(0); + + // Set initial output states + this->outdriver.write((uint16_t)0); + this->outdriver.enable(); + + this->display.write_backlight(100); + this->display.write(0x00); +} + +void hw::DccdHw::read(void) +{ + // Update low level inputs + this->board_hw.read(); + + this->counter.increment(); + + this->out_voltage = this->board_hw.out_voltage.last_read; + this->out_current = this->board_hw.out_current.last_read; + this->battery_voltage = this->board_hw.battery_voltage.last_read; + this->battery_current = this->board_hw.battery_current.last_read; + + this->btn_up.process(); + this->btn_down.process(); + this->btn_mode.process(); + this->handbrake.process(); + this->brakes.process(); + this->dimm.process(); + + this->pot.read(); +} + +/**** Private function definitions ***/ diff --git a/firmware/src/hw/dccd_hw.h b/firmware/src/hw/dccd_hw.h new file mode 100644 index 0000000..2a96053 --- /dev/null +++ b/firmware/src/hw/dccd_hw.h @@ -0,0 +1,64 @@ +#ifndef DCCD_HW_H_ +#define DCCD_HW_H_ + +/**** Includes ****/ +#include +#include "../bsp/board.h" +#include "../utils/vcounter.h" +#include "button.h" +#include "led_display.h" +#include "potentiometer.h" +#include "out_driver.h" + +namespace hw { + +/**** Public definitions ****/ +class DccdHw +{ + public: + typedef struct { + uint8_t handbrake_pull_up; + uint8_t speed_hall; + } dccdHwCfg_t; + + DccdHw(void); + ~DccdHw(void); + + void init(dccdHwCfg_t* cfg); + + // Inputs + uint16_t out_voltage; + uint16_t out_current; + uint16_t battery_voltage; + uint16_t battery_current; + + Button btn_up; + Button btn_down; + Button btn_mode; + Button handbrake; + Button brakes; + Button dimm; + + Potentiometer pot; + + // Outputs + LedDisplay display; + OutDriver outdriver; + + void read(void); + + #ifdef TESTING + protected: + #endif + bsp::Board board_hw; + util::VCounter counter; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DCCD_HW_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/devices.cpp b/firmware/src/hw/devices.cpp deleted file mode 100644 index a1a105b..0000000 --- a/firmware/src/hw/devices.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "devices.h" - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint16_t def_button_hold_time = 1000; -static const uint16_t def_max_voltage = 7000; -static const uint16_t def_min_voltage = 100; -static const uint16_t def_fuse_treshold = 6000; -static const uint16_t def_fuse_hold_cycles = 50; -static const uint16_t def_fuse_cooldown_cycles = 1000; - -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -void devices_init(void) -{ - board_init(); - - btn_up.hold_time = def_button_hold_time; - btn_down.hold_time = def_button_hold_time; - - ccout.max_voltage = def_max_voltage; - ccout.max_current = 0; - ccout.min_voltage = def_min_voltage; - - sup_fuse.hold_current = def_fuse_treshold; - sup_fuse.trip_cycles = def_fuse_hold_cycles; - sup_fuse.cooldown_cycles = def_fuse_cooldown_cycles; - - out_fuse.hold_current = def_fuse_treshold; - out_fuse.trip_cycles = def_fuse_hold_cycles; - out_fuse.cooldown_cycles = def_fuse_cooldown_cycles; - - hvdin3_pull.write(bsp::DOUT_HIGH); - - devices_update_inputs(); - - display.write(0x00); - display.set_brigthness(100); - - ccout.update(); - ccout.enable(); -} - -void devices_update_inputs(void) -{ - board_read(); - - pot.update(); - - btn_mode.update(); - btn_up.update(); - btn_down.update(); - - sw_dimm.update(); - sw_brakes.update(); - sw_hbrake.update(); - - sup_fuse.update(); - out_fuse.update(); - - display.process_timer(); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/devices.h b/firmware/src/hw/devices.h deleted file mode 100644 index 01395f3..0000000 --- a/firmware/src/hw/devices.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef HW_DEVICES_H_ -#define HW_DEVICES_H_ - -/**** Includes ****/ -#include - -#include "../bsp/board.h" - -#include "button.h" -#include "potentiometer.h" -#include "display_led.h" -#include "reg_out.h" -#include "fuse.h" - -static hw::Button btn_mode = hw::Button(&din1, bsp::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_up = hw::Button(&din4, bsp::DIN_LOW, 10, hw::BUTTON_OFF); -static hw::Button btn_down = hw::Button(&din3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); - -static hw::Button sw_dimm = hw::Button(&hvdin1, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_brakes = hw::Button(&hvdin2, bsp::DIN_HIGH, 10, hw::BUTTON_OFF); -static hw::Button sw_hbrake = hw::Button(&hvdin3, bsp::DIN_LOW, 10, hw::BUTTON_OFF); - -static hw::Potentiometer pot = hw::Potentiometer(&ain2, 500, 4500); - -static hw::DisplayLed display = hw::DisplayLed(&odout1, &odout2, &odout3, &odout4, &odout5, &odout6, &od_pwm); - -static hw::RegOut ccout = hw::RegOut(&hbridge, &bat_u, &dccd_u, &dccd_i); -static hw::Fuse sup_fuse = hw::Fuse(&bat_i); -static hw::Fuse out_fuse = hw::Fuse(&dccd_i); - -void devices_init(void); -void devices_update_inputs(void); - -#endif /* BSP_BOARD_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/display_led.cpp b/firmware/src/hw/display_led.cpp deleted file mode 100644 index af9085b..0000000 --- a/firmware/src/hw/display_led.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "display_led.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ -static uint8_t img_gen_dot10(uint8_t percent); -static uint8_t img_gen_dot20(uint8_t percent); -static uint8_t img_gen_bar(uint8_t percent); - -/**** Public function definitions ****/ -hw::DisplayLed::DisplayLed(bsp::DigitalOut* led0, bsp::DigitalOut* led1, bsp::DigitalOut* led2, bsp::DigitalOut* led3, bsp::DigitalOut* led4, bsp::DigitalOut* led5, bsp::PWMout* common) -{ - this->led0 = led0; - this->led1 = led1; - this->led2 = led2; - this->led3 = led3; - this->led4 = led4; - this->led5 = led5; - this->common = common; - - this->led0->write(0); - this->led1->write(0); - this->led2->write(0); - this->led3->write(0); - this->led4->write(0); - this->led5->write(0); - this->common->write(0); - - this->lock_counter = 0; - this->locked = 1; -} - -hw::DisplayLed::~DisplayLed(void) -{ - this->led0->write(0); - this->led1->write(0); - this->led2->write(0); - this->led3->write(0); - this->led4->write(0); - this->led5->write(0); - this->common->write(0); -} - -void hw::DisplayLed::show_percent(uint8_t percent, style_t style) -{ - uint8_t image = 0x00; - - switch(style) - { - case LED_DSP_BAR: - image = img_gen_bar(percent); - break; - - case LED_DSP_DOT10: - image = img_gen_dot10(percent); - break; - - default: - image = img_gen_dot20(percent); - break; - } - - this->write(image); -} - -void hw::DisplayLed::write(uint8_t image) -{ - if(image&0x01) this->led0->write(1); - else this->led0->write(0); - - if(image&0x02) this->led1->write(1); - else this->led1->write(0); - - if(image&0x04) this->led2->write(1); - else this->led2->write(0); - - if(image&0x08) this->led3->write(1); - else this->led3->write(0); - - if(image&0x10) this->led4->write(1); - else this->led4->write(0); - - if(image&0x20) this->led5->write(1); - else this->led5->write(0); -} - -void hw::DisplayLed::set_brigthness(uint8_t percent) -{ - this->common->write(percent); -} - -void hw::DisplayLed::set_lock(uint16_t timeout) -{ - this->lock_counter = timeout; - this->locked = 1; -} - -void hw::DisplayLed::process_timer(void) -{ - if(this->lock_counter) this->lock_counter--; - else this->locked = 0; -} - -/**** Private function definitions ****/ -static uint8_t img_gen_dot10(uint8_t percent) -{ - switch(percent) - { - case 0 ... 5: - return 0x01; - - case 6 ... 15: - return 0x03; - - case 16 ... 25: - return 0x02; - - case 26 ... 35: - return 0x06; - - case 36 ... 45: - return 0x04; - - case 46 ... 55: - return 0x0C; - - case 56 ... 65: - return 0x08; - - case 66 ... 75: - return 0x18; - - case 76 ... 85: - return 0x10; - - case 86 ... 95: - return 0x30; - - default: - return 0x20; - } -} - -static uint8_t img_gen_dot20(uint8_t percent) -{ - switch(percent) - { - case 0 ... 10: - return 0x01; - - case 11 ... 30: - return 0x02; - - case 31 ... 50: - return 0x04; - - case 51 ... 70: - return 0x08; - - case 71 ... 90: - return 0x10; - - default: - return 0x20; - } -} - -static uint8_t img_gen_bar(uint8_t percent) -{ - switch(percent) - { - case 0 ... 10: - return 0x01; - - case 11 ... 30: - return 0x03; - - case 31 ... 50: - return 0x07; - - case 51 ... 70: - return 0x0F; - - case 71 ... 90: - return 0x1F; - - default: - return 0x3F; - } -} \ No newline at end of file diff --git a/firmware/src/hw/display_led.h b/firmware/src/hw/display_led.h deleted file mode 100644 index 6e73567..0000000 --- a/firmware/src/hw/display_led.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef DISPLAY_LED_H_ -#define DISPLAY_LED_H_ - -/**** Includes ****/ -#include -#include "../bsp/dout.h" -#include "../bsp/pwm.h" - -namespace hw { - -/**** Public definitions ****/ - -class DisplayLed -{ - protected: - bsp::DigitalOut* led0; - bsp::DigitalOut* led1; - bsp::DigitalOut* led2; - bsp::DigitalOut* led3; - bsp::DigitalOut* led4; - bsp::DigitalOut* led5; - bsp::PWMout* common; - - uint16_t lock_counter; - - public: - typedef enum { - LED_DSP_DOT20, - LED_DSP_DOT10, - LED_DSP_BAR - } style_t; - - DisplayLed(bsp::DigitalOut* led0, bsp::DigitalOut* led1, bsp::DigitalOut* led2, bsp::DigitalOut* led3, bsp::DigitalOut* led4, bsp::DigitalOut* led5, bsp::PWMout* common); - ~DisplayLed(void); - - void show_percent(uint8_t percent, style_t style); - void write(uint8_t image); - - void set_brigthness(uint8_t percent); - - void set_lock(uint16_t timeout); - void process_timer(void); - - uint8_t locked; -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* DISPLAY_LED_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/fuse.cpp b/firmware/src/hw/fuse.cpp deleted file mode 100644 index 51d8cae..0000000 --- a/firmware/src/hw/fuse.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "fuse.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::Fuse::Fuse(bsp::AnalogIn* ain_ch) -{ - this->hold_current = 0; - this->trip_cycles = 0; - this->warning = 0; - this->fault = 0; - this->cooldown_counter = 0; - this->cooldown_cycles = 0; - this->retry_cnt = 0; -} - -hw::Fuse::~Fuse(void) -{ - return; -} - -void hw::Fuse::update(void) -{ - // Under threshold - if(this->ain_ch->last_read <= this->hold_current) - { - // Clear warning flag - this->warning = 0; - - // OC energy counter - if(this->oc_counter > 0) this->oc_counter--; - - // Cool down fuse - if(this->cooldown_counter > 0) this->cooldown_counter--; - - // Auto reset logic - if((this->fault)&&(this->cooldown_counter==0)) - { - this->fault = 0; - this->retry_cnt = util::sat_add(this->retry_cnt, 1); - }; - return; - }; - - // Over current condition - this->warning = 1; - - // PC energy counter - this->oc_counter = util::sat_add(this->oc_counter, 1); - - // Check for trip threshold - if(this->oc_counter < this->trip_cycles) return; - - // Trip fuse - this->fault = 1; - this->cooldown_counter = this->cooldown_cycles; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/fuse.h b/firmware/src/hw/fuse.h deleted file mode 100644 index ae789c8..0000000 --- a/firmware/src/hw/fuse.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef FUSE_H_ -#define FUSE_H_ - -/**** Includes ****/ -#include -#include "../bsp/ain.h" - -namespace hw { - -/**** Public definitions ****/ - -class Fuse -{ - protected: - bsp::AnalogIn* ain_ch; - uint16_t oc_counter; - uint16_t cooldown_counter; - - public: - Fuse(bsp::AnalogIn* ain_ch); - ~Fuse(void); - - uint16_t hold_current; - uint16_t trip_cycles; - uint8_t warning; - uint8_t fault; - uint16_t cooldown_cycles; - uint8_t retry_cnt; - - void update(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* POTENTIOMETER_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/led_display.cpp b/firmware/src/hw/led_display.cpp new file mode 100644 index 0000000..867e7f0 --- /dev/null +++ b/firmware/src/hw/led_display.cpp @@ -0,0 +1,157 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "led_display.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +hw::LedDisplay::LedDisplay(void) +{ + return; +} + +hw::LedDisplay::~LedDisplay(void) +{ + this->force(0x00); + this->write_backlight(0); +} + +void hw::LedDisplay::init(doutCfg_t* dout_chs, uint8_t act_lvl, util::VCounter* timer, bsp::PwmOut* pwm_ch) +{ + this->led0_dout_ch = dout_chs->led0_dout_ch; + this->led1_dout_ch = dout_chs->led1_dout_ch; + this->led2_dout_ch = dout_chs->led2_dout_ch; + this->led3_dout_ch = dout_chs->led3_dout_ch; + this->led4_dout_ch = dout_chs->led4_dout_ch; + this->led5_dout_ch = dout_chs->led5_dout_ch; + + if(act_lvl) this->act_lvl = 1; + else this->act_lvl = 0; + + this->timer = timer; + + this->pwm_ch = pwm_ch; + + this->on_time = 0; + this->period = 0; + this->cycle_cnt = 0; + this->cycle_limit = 0; + this->timestamp_start = 0; + + this->force(0x00); + this->write_backlight(0); +} + +void hw::LedDisplay::force(uint8_t image) +{ + uint8_t led_state; + + if(image&0x01) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led0_dout_ch); + + if(image&0x02) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led1_dout_ch); + + if(image&0x04) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led2_dout_ch); + + if(image&0x08) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led3_dout_ch); + + if(image&0x10) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led4_dout_ch); + + if(image&0x20) led_state = 1; + else led_state = 0; + this->set_single_led(led_state, this->led5_dout_ch); +} + +void hw::LedDisplay::write(uint8_t image) +{ + // Static mode + this->on_time = 1; + this->period = 0; + this->cycle_cnt = 0; + this->cycle_limit = 0; + this->timestamp_start = 0; + + // Set initial state + this->force(image); +} + +void hw::LedDisplay::write(uint8_t image, uint16_t on_time, uint16_t period, uint8_t cycle_limit) +{ + // "PWM" mode + this->on_time = on_time; + this->period = period; + this->cycle_cnt = 0; + this->cycle_limit = cycle_limit; + + // Set initial state + if(this->on_time > 0) this->force(image); + else this->force(0x00); + + // Cycle start time + this->timestamp_start = this->timer->read(); +} + +void hw::LedDisplay::process(void) +{ + if(this->period == 0) return; // Nothing to do + + // Update cycle timing + uint16_t ts_now = this->timer->read(); + uint16_t td = util::time_delta(this->timestamp_start, ts_now); + uint32_t td_ms = this->timer->convert_ms(td); + + if(td_ms >= this->period) + { + this->timestamp_start = ts_now; + this->cycle_cnt++; + }; + + // Check cycle limit + if((this->cycle_cnt >= this->cycle_limit)&&(this->cycle_limit)) + { + this->on_time = 0; + this->period = 0; + this->timestamp_start = 0; + this->force(0x00); + return; + }; + + // Do output compare + if(td_ms < this->on_time) this->force(this->image); + else this->force(0x00); +} + +uint8_t hw::LedDisplay::is_cycle_end(void) +{ + if(this->cycle_cnt >= this->cycle_limit) return 1; + else return 0; +} + +void hw::LedDisplay::write_backlight(uint8_t percent) +{ + this->pwm_ch->write(percent); +} + +void hw::LedDisplay::set_single_led(uint8_t state, bsp::DigitalOut* led_ch) +{ + uint8_t lvl = 0; + + if(((state==0)&&(this->act_lvl==0))||((state!=0)&&(this->act_lvl==1))) lvl = 1; + + led_ch->write(lvl); +} + +/**** Private function definitions ***/ diff --git a/firmware/src/hw/led_display.h b/firmware/src/hw/led_display.h new file mode 100644 index 0000000..564d163 --- /dev/null +++ b/firmware/src/hw/led_display.h @@ -0,0 +1,66 @@ +#ifndef LED_DISPLAY_H_ +#define LED_DISPLAY_H_ + +/**** Includes ****/ +#include +#include "../utils/vcounter.h" +#include "../bsp/board.h" + +namespace hw { + +/**** Public definitions ****/ +class LedDisplay +{ + public: + typedef struct { + bsp::DigitalOut* led0_dout_ch; + bsp::DigitalOut* led1_dout_ch; + bsp::DigitalOut* led2_dout_ch; + bsp::DigitalOut* led3_dout_ch; + bsp::DigitalOut* led4_dout_ch; + bsp::DigitalOut* led5_dout_ch; + } doutCfg_t; + + LedDisplay(void); + ~LedDisplay(void); + + uint16_t on_time; + uint16_t period; + uint8_t cycle_cnt; + uint8_t cycle_limit; + + void init(doutCfg_t* dout_chs, uint8_t act_lvl, util::VCounter* timer, bsp::PwmOut* pwm_ch); + void write(uint8_t image); + void write(uint8_t image, uint16_t on_time, uint16_t period, uint8_t cycle_limit); + void process(void); + uint8_t is_cycle_end(void); + void force(uint8_t image); + + void write_backlight(uint8_t percent); + + #ifdef TESTING + protected: + #endif + bsp::DigitalOut* led0_dout_ch; + bsp::DigitalOut* led1_dout_ch; + bsp::DigitalOut* led2_dout_ch; + bsp::DigitalOut* led3_dout_ch; + bsp::DigitalOut* led4_dout_ch; + bsp::DigitalOut* led5_dout_ch; + uint8_t act_lvl; + util::VCounter* timer; + bsp::PwmOut* pwm_ch; + uint16_t timestamp_start; + uint8_t image; + + void set_single_led(uint8_t state, bsp::DigitalOut* led_ch); +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* LED_DISPLAY_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/out_driver.cpp b/firmware/src/hw/out_driver.cpp new file mode 100644 index 0000000..6701ce6 --- /dev/null +++ b/firmware/src/hw/out_driver.cpp @@ -0,0 +1,105 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "out_driver.h" + +using namespace hw; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::OutDriver::OutDriver(void) +{ + return; +} + +hw::OutDriver::~OutDriver(void) +{ + return; +} + +void hw::OutDriver::init(bsp::PwmOut* pwm_high, bsp::DigitalOut* dout_low) +{ + this->pwm_high = pwm_high; + this->dout_low = dout_low; + + this->target_duty = 0; + this->target_low = 0; + this->disabled = 1; +} + +void hw::OutDriver::write(uint16_t numerator) +{ + this->target_duty = numerator; + this->target_low = 1; + + // Check if enabled + if(this->disabled) + { + return; + }; + + // Set low side + if(this->dout_low->last_writen == 0) + { + this->dout_low->write(this->target_low); + }; + + // Set PWM + this->pwm_high->write(this->target_duty); +} + +void hw::OutDriver::write(uint8_t percent) +{ + // Convert to numerator/0xFFFF + this->write(util::percent_to_16b(percent)); +} + +void hw::OutDriver::write_hiz(void) +{ + this->target_duty = 0; + this->target_low = 0; + + // Check if enabled + if(this->disabled) + { + return; + }; + + // Set PWM + this->pwm_high->write((uint16_t)0); + + // Set low side + this->dout_low->write(0); +} + +void hw::OutDriver::enable(void) +{ + if(this->disabled==0) return; + this->disabled = 0; + + if(this->target_low==0) this->write_hiz(); + else this->write(this->target_duty); +} + +void hw::OutDriver::disable(void) +{ + if(this->disabled!=0) return; + + // Set PWM + this->pwm_high->write((uint16_t)0); + + // Set low side + this->dout_low->write(0); + + this->disabled = 1; +} + +uint8_t hw::OutDriver::is_disabled(void) +{ + return this->disabled; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/out_driver.h b/firmware/src/hw/out_driver.h new file mode 100644 index 0000000..b6418e9 --- /dev/null +++ b/firmware/src/hw/out_driver.h @@ -0,0 +1,45 @@ +#ifndef OUT_DRIVER_H_ +#define OUT_DRIVER_H_ + +/**** Includes ****/ +#include +#include "../bsp/board.h" + +namespace hw { + +/**** Public definitions ****/ + +class OutDriver +{ + public: + OutDriver(void); + ~OutDriver(void); + + void init(bsp::PwmOut* pwm_high, bsp::DigitalOut* dout_low); + + uint16_t target_duty; + uint8_t target_low; + + void write(uint16_t numerator); + void write(uint8_t percent); + void write_hiz(void); + void enable(void); + void disable(void); + uint8_t is_disabled(void); + + #ifndef TESTING + protected: + #endif + bsp::PwmOut* pwm_high; + bsp::DigitalOut* dout_low; + uint8_t disabled; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* OUT_DRIVER_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/potentiometer.cpp b/firmware/src/hw/potentiometer.cpp index 617a1b5..2ddbc11 100644 --- a/firmware/src/hw/potentiometer.cpp +++ b/firmware/src/hw/potentiometer.cpp @@ -11,12 +11,9 @@ using namespace hw; /**** Private function declarations ****/ /**** Public function definitions ****/ -hw::Potentiometer::Potentiometer(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) +hw::Potentiometer::Potentiometer(void) { - this->ain_ch = ain_ch; - this->low_deadzone = low_deadzone; - this->high_deadzone = high_deadzone; - this->percent = 0; + return; } hw::Potentiometer::~Potentiometer(void) @@ -24,14 +21,26 @@ hw::Potentiometer::~Potentiometer(void) return; } -uint8_t hw::Potentiometer::update(void) +void hw::Potentiometer::init(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone) { - // Calculate percent - if(this->ain_ch->last_read <= this->low_deadzone) this->percent = 0; - else if(this->ain_ch->last_read >= this->high_deadzone ) this->percent = 100; - else this->percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); + this->ain_ch = ain_ch; + this->low_deadzone = low_deadzone; + this->high_deadzone = high_deadzone; + this->last_percent = 0; + this->update_ain = 1; +} + +uint8_t hw::Potentiometer::read(void) +{ + // Update analog input + if(this->update_ain) this->ain_ch->read(); - return this->percent; + // Calculate percent + if(this->ain_ch->last_read <= this->low_deadzone) this->last_percent = 0; + else if(this->ain_ch->last_read >= this->high_deadzone ) this->last_percent = 100; + else this->last_percent = util::interpolate(this->ain_ch->last_read, this->low_deadzone, this->high_deadzone, 0, 100); + + return this->last_percent; } /**** Private function definitions ****/ diff --git a/firmware/src/hw/potentiometer.h b/firmware/src/hw/potentiometer.h index 2321f7f..be65f4d 100644 --- a/firmware/src/hw/potentiometer.h +++ b/firmware/src/hw/potentiometer.h @@ -3,26 +3,31 @@ /**** Includes ****/ #include -#include "../bsp/ain.h" +#include "../bsp/board.h" namespace hw { /**** Public definitions ****/ class Potentiometer -{ - protected: - bsp::AnalogIn* ain_ch; - +{ public: - Potentiometer(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone); + Potentiometer(void); ~Potentiometer(void); + void init(bsp::AnalogIn* ain_ch, uint16_t low_deadzone, uint16_t high_deadzone); + uint16_t low_deadzone; uint16_t high_deadzone; - uint8_t percent; + uint8_t last_percent; + uint8_t update_ain; - uint8_t update(void); + uint8_t read(void); + + #ifndef TESTING + protected: + #endif + bsp::AnalogIn* ain_ch; }; /**** Public function declarations ****/ diff --git a/firmware/src/hw/reg_out.cpp b/firmware/src/hw/reg_out.cpp deleted file mode 100644 index 9edc744..0000000 --- a/firmware/src/hw/reg_out.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "reg_out.h" - -using namespace hw; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -hw::RegOut::RegOut(bsp::Hafbridge* hbridge, bsp::AnalogIn* sup_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i) -{ - this->hbridge = hbridge; - this->supply_voltage = sup_u; - this->out_voltage = out_u; - this->out_currnet = out_i; - - this->max_voltage = 6750; - this->max_current = 4500; - this->min_voltage = 200; - - this->set_voltage = 0; - this->out_impedance = 0xFFFF; - - this->hbridge->disable(); -} - -hw::RegOut::~RegOut(void) -{ - this->hbridge->write((uint16_t)0x0000); - this->hbridge->disable(); - return; -} - -void hw::RegOut::update(void) -{ - // Calculate output impedance - if((this->out_currnet == 0)||(this->out_voltage->last_read == 0)) this->out_impedance = 0xFFFF; - else this->out_impedance = util::sat_div_kilo(this->out_voltage->last_read, this->out_currnet->last_read); - - // Recalculate output set voltage - if((this->max_voltage==0)||(this->max_current==0)) this->set_voltage = 0; - else this->set_voltage = util::sat_mul_kilo(this->max_current, this->out_impedance); - - // Limit set voltage - if(this->set_voltage > this->max_voltage) this->set_voltage = this->max_voltage; - - if((this->set_voltage>0)&&(this->set_voltage < this->min_voltage)) this->set_voltage = this->min_voltage; - - // Set output - this->hbridge->write(util::sat_ratio(this->set_voltage, this->supply_voltage->last_read)); -} - -void hw::RegOut::enable(void) -{ - this->hbridge->enable(); -} - -void hw::RegOut::disable(void) -{ - this->hbridge->disable(); -} - -uint8_t hw::RegOut::is_enabled(void) -{ - return this->hbridge->is_enabled(); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/hw/reg_out.h b/firmware/src/hw/reg_out.h deleted file mode 100644 index f2356c7..0000000 --- a/firmware/src/hw/reg_out.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef REGULATED_OUTPUT_H_ -#define REGULATED_OUTPUT_H_ - -/**** Includes ****/ -#include -#include "../bsp/ain.h" -#include "../bsp/halfbridge.h" - -namespace hw { - -/**** Public definitions ****/ - -class RegOut -{ - protected: - bsp::Hafbridge* hbridge; - bsp::AnalogIn* supply_voltage; - bsp::AnalogIn* out_voltage; - bsp::AnalogIn* out_currnet; - - public: - RegOut(bsp::Hafbridge* hbridge, bsp::AnalogIn* sup_u, bsp::AnalogIn* out_u, bsp::AnalogIn* out_i); - ~RegOut(void); - - uint16_t max_voltage; - uint16_t max_current; - uint16_t min_voltage; - - uint16_t set_voltage; - uint16_t out_impedance; - - void update(void); - void enable(void); - void disable(void); - uint8_t is_enabled(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* REGULATED_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/button_force.cpp b/firmware/src/logic/button_force.cpp deleted file mode 100644 index 59f70ad..0000000 --- a/firmware/src/logic/button_force.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "button_force.h" - -using namespace logic; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -logic::ButtonForce::ButtonForce(hw::Button* btn_up, hw::Button* btn_down) -{ - this->btn_up = btn_up; - this->btn_down = btn_down; - this->force = 0; - this->step = 10; - this->is_new = 0; -} - -logic::ButtonForce::~ButtonForce(void) -{ - return; -} - -uint8_t logic::ButtonForce::update(void) -{ - uint8_t next_force = 0; - - if((this->btn_up->is_new)&&(this->btn_up->state == hw::BUTTON_ON)) - { - next_force = util::sat_add(this->force, this->step); - if(next_force > 100) next_force = 100; - this->btn_up->is_new = 0; - }; - - if((this->btn_down->is_new)&&(this->btn_down->state == hw::BUTTON_ON)) - { - next_force = util::sat_subtract(this->force, this->step); - this->btn_down->is_new = 0; - }; - - if(next_force != this->force) this->is_new = 1; - - this->force = next_force; - - return this->force; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/logic/button_force.h b/firmware/src/logic/button_force.h deleted file mode 100644 index 4dd81e7..0000000 --- a/firmware/src/logic/button_force.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef BUTTON_FORCE_H_ -#define BUTTON_FORCE_H_ - -/**** Includes ****/ -#include -#include "../hw/button.h" - -namespace logic { - -/**** Public definitions ****/ - -class ButtonForce -{ - protected: - hw::Button* btn_up; - hw::Button* btn_down; - - public: - ButtonForce(hw::Button* btn_up, hw::Button* btn_down); - ~ButtonForce(void); - - uint8_t force; - uint8_t step; - uint8_t is_new; - - uint8_t update(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* BUTTON_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/cfg_mem.cpp b/firmware/src/logic/cfg_mem.cpp deleted file mode 100644 index eea5229..0000000 --- a/firmware/src/logic/cfg_mem.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/**** Includes ****/ -#include "cfg_mem.h" -#include "../bsp/mcu/mcu_hal.h" - -using namespace logic; - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint16_t addr_btn_force = 0x0000; -static const uint16_t addr_bmode = 0x0001; -static const uint16_t addr_pot_mode = 0x0002; -static const uint16_t addr_dsp_brigth = 0x0003; -static const uint16_t addr_dsp_dimm = 0x0004; -static const uint16_t addr_brake_force = 0x0005; -static const uint16_t addr_max_hbrake_time = 0x0006; -static const uint16_t addr_lock_current = 0x0008; -static const uint16_t addr_max_out_voltage = 0x000A; -static const uint16_t addr_min_out_voltage = 0x000C; -static const uint16_t addr_cfg_good = 0x000D; - -static const uint8_t def_btn_force = 0; -static const uint8_t def_pot_mode = 0; -static const uint8_t def_bmode = 0; -static const uint8_t def_dimm = 50; -static const uint8_t def_brigth = 100; -static const uint8_t def_brake_force = 100; -static const uint16_t def_max_hbrake_time = 1000; -static const uint16_t def_lock_current = 4500; -static const uint16_t def_max_out_voltage = 7000; -static const uint16_t def_min_out_voltage = 200; - -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -logic::CfgMemory::CfgMemory(void) -{ - this->cfg_good = 0; - - this->mem_btn_force = 0; - this->mem_bmode = 0; - this->mem_pot_mode = 0; - this->mem_dsp_brigth = 0; - this->mem_dsp_dimm = 0; - this->mem_brake_force = 0; - this->mem_max_hbrake_time = 0; - this->mem_lock_current = 0; - this->mem_max_out_voltage = 0; - this->mem_min_out_voltage = 0; - - this->restore(); -} - -logic::CfgMemory::~CfgMemory(void) -{ - return; -} - -void logic::CfgMemory::init(void) -{ - uint8_t cfg_good_magic = mcu::eeprom_read8b(addr_cfg_good); - this->mem_btn_force = mcu::eeprom_read8b(addr_btn_force); - this->mem_bmode = mcu::eeprom_read8b(addr_bmode); - this->mem_pot_mode = mcu::eeprom_read8b(addr_pot_mode); - this->mem_dsp_brigth = mcu::eeprom_read8b(addr_dsp_brigth); - this->mem_dsp_dimm = mcu::eeprom_read8b(addr_dsp_dimm); - this->mem_brake_force = mcu::eeprom_read8b(addr_brake_force); - this->mem_max_hbrake_time = mcu::eeprom_read16b(addr_max_hbrake_time); - this->mem_lock_current = mcu::eeprom_read16b(addr_lock_current); - this->mem_max_out_voltage = mcu::eeprom_read16b(addr_max_out_voltage); - this->mem_min_out_voltage = mcu::eeprom_read16b(addr_min_out_voltage); - - // Validate EEPROM data - if(cfg_good_magic == 0x37) this->cfg_good = 1; - else this->cfg_good = 0; - - if(this->cfg_good != 1) - { - this->mem_btn_force = def_btn_force; - this->mem_bmode = def_bmode; - this->mem_pot_mode = def_pot_mode; - this->mem_dsp_brigth = def_brigth; - this->mem_dsp_dimm = def_dimm; - this->mem_brake_force = def_brake_force; - this->mem_max_hbrake_time = def_max_hbrake_time; - this->mem_lock_current = def_lock_current; - this->mem_max_out_voltage = def_max_out_voltage; - this->mem_min_out_voltage = def_min_out_voltage; - } - - this->restore(); -} - -void logic::CfgMemory::save(void) -{ - if(this->btn_force != this->mem_btn_force) - { - this->mem_btn_force = this->btn_force; - mcu::eeprom_write8b(addr_btn_force, this->mem_btn_force); - }; - - if(this->bmode != this->mem_bmode) - { - this->mem_bmode = this->bmode; - mcu::eeprom_write8b(addr_bmode, this->mem_bmode); - }; -} - -void logic::CfgMemory::save_all(void) -{ - this->save(); - - if(this->pot_mode != this->mem_pot_mode) - { - this->mem_pot_mode = this->pot_mode; - mcu::eeprom_write8b(addr_pot_mode, this->mem_pot_mode); - }; - - if(this->dsp_brigth != this->mem_dsp_brigth) - { - this->mem_dsp_brigth = this->dsp_brigth; - mcu::eeprom_write8b(addr_dsp_brigth, this->mem_dsp_brigth); - }; - - if(this->dsp_dimm != this->mem_dsp_dimm) - { - this->mem_dsp_dimm = this->dsp_dimm; - mcu::eeprom_write8b(addr_dsp_dimm, this->mem_dsp_dimm); - }; - - if(this->brake_force != this->mem_brake_force) - { - this->mem_brake_force = this->brake_force; - mcu::eeprom_write8b(addr_brake_force, this->mem_brake_force); - }; - - if(this->max_hbrake_time != this->mem_max_hbrake_time) - { - this->mem_max_hbrake_time = this->max_hbrake_time; - mcu::eeprom_write16b(addr_max_hbrake_time, this->mem_max_hbrake_time); - }; - - if(this->lock_current != this->mem_lock_current) - { - this->mem_lock_current = this->lock_current; - mcu::eeprom_write16b(addr_lock_current, this->mem_lock_current); - }; - - if(this->max_out_voltage != this->mem_max_out_voltage) - { - this->mem_max_out_voltage = this->max_out_voltage; - mcu::eeprom_write16b(addr_max_out_voltage, this->mem_max_out_voltage); - }; - - if(this->min_out_voltage != this->mem_min_out_voltage) - { - this->mem_min_out_voltage = this->min_out_voltage; - mcu::eeprom_write16b(addr_min_out_voltage, this->mem_min_out_voltage); - }; -} - -void logic::CfgMemory::restore(void) -{ - this->btn_force = this->mem_btn_force; - this->bmode = this->mem_bmode; - this->pot_mode = this->mem_pot_mode; - this->dsp_brigth = this->mem_dsp_brigth; - this->dsp_dimm = this->mem_dsp_dimm; - this->brake_force = this->mem_brake_force; - this->max_hbrake_time = this->mem_max_hbrake_time; - this->lock_current = this->mem_lock_current; - this->max_out_voltage = this->mem_max_out_voltage; - this->min_out_voltage = this->mem_min_out_voltage; -} - -uint8_t logic::CfgMemory::checksum(void) -{ - uint32_t cs = 0; - cs += (uint32_t)this->mem_btn_force; - cs += (uint32_t)this->mem_bmode; - cs += (uint32_t)this->mem_pot_mode; - cs += (uint32_t)this->mem_dsp_brigth; - cs += (uint32_t)this->mem_dsp_dimm; - cs += (uint32_t)this->mem_brake_force; - cs += (uint32_t)this->mem_max_hbrake_time; - cs += (uint32_t)this->mem_lock_current; - cs += (uint32_t)this->mem_max_out_voltage; - cs += (uint32_t)this->mem_min_out_voltage; - - return (uint8_t)cs; -} - - -/**** Private function definitions ****/ diff --git a/firmware/src/logic/cfg_mem.h b/firmware/src/logic/cfg_mem.h deleted file mode 100644 index 97460a4..0000000 --- a/firmware/src/logic/cfg_mem.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef CONFIG_H_ -#define CONFIG_H_ - -/**** Includes ****/ -#include -#include "../hw/button.h" - -namespace logic { - -/**** Public definitions ****/ - -class CfgMemory -{ - protected: - uint8_t mem_btn_force; - uint8_t mem_bmode; - uint8_t mem_pot_mode; - uint8_t mem_dsp_brigth; - uint8_t mem_dsp_dimm; - uint8_t mem_brake_force; - uint16_t mem_max_hbrake_time; - uint16_t mem_lock_current; - uint16_t mem_max_out_voltage; - uint16_t mem_min_out_voltage; - - public: - CfgMemory(void); - ~CfgMemory(void); - - uint8_t cfg_good; - uint8_t btn_force; - uint8_t bmode; - uint8_t pot_mode; - uint8_t dsp_brigth; - uint8_t dsp_dimm; - uint8_t brake_force; - uint16_t max_hbrake_time; - uint16_t lock_current; - uint16_t max_out_voltage; - uint16_t min_out_voltage; - - void init(void); - void save(void); - void save_all(void); - void restore(void); - uint8_t checksum(void); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* DCCD_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/logic/dccd_force.cpp b/firmware/src/logic/dccd_force.cpp deleted file mode 100644 index cedebe4..0000000 --- a/firmware/src/logic/dccd_force.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "dccd_force.h" - -using namespace logic; - -/**** Private definitions ****/ -/**** Private constants ****/ -static const uint16_t def_max_hbrake_time = 1000; -static const uint16_t def_brake_force = 100; - -/**** Private variables ****/ -/**** Private function declarations ****/ -/**** Public function definitions ****/ -logic::DccdForce::DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::Button* sw_brakes) -{ - this->mode = btn_mode; - this->handbrake = sw_hbrake; - this->brakes = sw_brakes; - - this->is_new = 0; - this->force = 0; - this->brake_mode = 0; - - this->max_hbrake_time = def_max_hbrake_time; - this->brake_force = def_brake_force; -} - -logic::DccdForce::~DccdForce(void) -{ - return; -} - -uint8_t logic::DccdForce::update(uint8_t user_force) -{ - // Process mode button - if((this->mode->is_new)&&(this->mode->state == hw::BUTTON_ON)) - { - // Cycle brake mode - switch(this->brake_mode) - { - case 0: - this->brake_mode = 1; - - case 1: - this->brake_mode = 2; - - default: - this->brake_mode = 0; - } - - this->mode->is_new = 0; - this->is_new_bmode = 1; - }; - - // Determine target force source - uint8_t next_force = user_force; - if((this->handbrake->state == hw::BUTTON_ON)&&((this->handbrake->time < this->max_hbrake_time)||(this->max_hbrake_time == 0))) - { - next_force = 0; - } - else if(this->brakes->state == hw::BUTTON_ON) - { - switch(this->brake_mode) - { - case 0: - next_force = 0; - - case 1: - next_force = user_force; - - default: - next_force = this->brake_force; - } - }; - - if(next_force != this->force) this->is_new = 1; - - this->force = next_force; - - return this->force; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/logic/dccd_force.h b/firmware/src/logic/dccd_force.h deleted file mode 100644 index c5291af..0000000 --- a/firmware/src/logic/dccd_force.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef DCCD_FORCE_H_ -#define DCCD_FORCE_H_ - -/**** Includes ****/ -#include -#include "../hw/button.h" - -namespace logic { - -/**** Public definitions ****/ - -class DccdForce -{ - protected: - hw::Button* mode; - hw::Button* handbrake; - hw::Button* brakes; - - public: - DccdForce(hw::Button* btn_mode, hw::Button* sw_hbrake, hw::Button* sw_brakes); - ~DccdForce(void); - - uint8_t force; - uint8_t is_new; - - uint8_t brake_mode; - uint8_t is_new_bmode; - - uint16_t max_hbrake_time; - uint8_t brake_force; - - uint8_t update(uint8_t user_force); -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* DCCD_FORCE_H_ */ \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 96aa6c5..9dcfdd8 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,122 +1,32 @@ /**** Includes ****/ #include "utils/utils.h" -#include "hw/devices.h" - -#include "logic/button_force.h" -#include "logic/dccd_force.h" - -#include "logic/cfg_mem.h" +#include "hw/dccd_hw.h" /**** Private definitions ****/ /**** Private constants ****/ -static const uint16_t dsp_lock_bmode = 1000; -static const uint16_t dsp_lock_force = 500; - /**** Private variables ****/ -static logic::CfgMemory cfg_mem = logic::CfgMemory(); - -static logic::ButtonForce button_force = logic::ButtonForce(&btn_up, &btn_down); -static logic::DccdForce dccd_force = logic::DccdForce(&btn_mode, &sw_hbrake, &sw_brakes); +hw::DccdHw dccd_hw; /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) { - // HW setup - devices_init(); + // Setup + hw::DccdHw::dccdHwCfg_t hw_cfg; - // Read saved config - cfg_mem.init(); + hw_cfg.handbrake_pull_up = 0; + hw_cfg.speed_hall = 0; - if(cfg_mem.cfg_good !=1 ) - { - cfg_mem.btn_force = 0; - cfg_mem.bmode = 0; - cfg_mem.pot_mode = 0; - cfg_mem.dsp_brigth = 100; - cfg_mem.dsp_dimm = 50; - cfg_mem.brake_force = 100; - cfg_mem.max_hbrake_time = 1000; - cfg_mem.lock_current = 4200; - cfg_mem.max_out_voltage = 6500; - cfg_mem.min_out_voltage = 500; - cfg_mem.save_all(); - }; - - uint8_t user_force = 0; - - ccout.max_voltage = cfg_mem.max_out_voltage; - ccout.min_voltage = cfg_mem.min_out_voltage; - - button_force.force = cfg_mem.btn_force; - dccd_force.brake_mode = cfg_mem.bmode; - dccd_force.max_hbrake_time = cfg_mem.max_hbrake_time; - dccd_force.brake_force = cfg_mem.brake_force; + dccd_hw.init(&hw_cfg); // Super loop while(1) - { + { // Update inputs - devices_update_inputs(); + dccd_hw.read(); - // Update user setting - button_force.update(); - - // Select user force input - if(cfg_mem.pot_mode) user_force = pot.percent; - else user_force = button_force.force; - - // Calculate next target force - dccd_force.update(user_force); - - // Override force in case of fault - if((sup_fuse.fault)||(out_fuse.fault)) dccd_force.force = 0; - - // Convert force to current - ccout.max_current = util::percent_of(dccd_force.force, cfg_mem.lock_current); - - // Execute outputs - ccout.update(); - - // Set display - if(dccd_force.is_new_bmode) - { - uint8_t bmode_img = 0x03; - switch(dccd_force.brake_mode) - { - case 1: - bmode_img = 0x0C; - break; - - case 2: - bmode_img = 0x30; - break; - - default: - bmode_img = 0x03; - break; - } - display.write(bmode_img); - display.set_lock(dsp_lock_bmode); - dccd_force.is_new_bmode = 0; - } - else if((button_force.is_new)&&(cfg_mem.pot_mode==0)) - { - display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); - display.set_lock(dsp_lock_force); - button_force.is_new = 0; - } - else if(display.locked==0) display.show_percent(dccd_force.force, hw::DisplayLed::LED_DSP_DOT10); - - // Process dimm - if(sw_dimm.state == hw::BUTTON_ON) display.set_brigthness(cfg_mem.dsp_dimm); - else display.set_brigthness(cfg_mem.dsp_brigth); - - // Save user setting changes - cfg_mem.btn_force = button_force.force; - cfg_mem.bmode = dccd_force.brake_mode; - cfg_mem.save(); + // Do something continue; // End of super loop } diff --git a/firmware/src/sort/cv_out.cpp b/firmware/src/sort/cv_out.cpp new file mode 100644 index 0000000..4800e9d --- /dev/null +++ b/firmware/src/sort/cv_out.cpp @@ -0,0 +1,50 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "cv_out.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +bsp::CVOut::CVOut(void) +{ + return; +} + +bsp::CVOut::~CVOut(void) +{ + return; +} + +void bsp::CVOut::init(AnalogIn* ain_ch, Halfbridge* out_ch) +{ + this->ain_ch = ain_ch; + this->out_ch = out_ch; + + this->voltage = 0; + + this->disabled = 1; + this->process_ain = 1; +} + +void bsp::CVOut::process(void) +{ + // Update analog input + if(this->process_ain) this->ain_ch->read(); + + // Check to HiZ output + if(this->disabled) + { + this->out_ch->write_hiz(); + return; + }; + + // Calculate and apply necessary PWM + this->out_ch->write(util::sat_ratio(this->voltage, this->ain_ch->last_read)); +} + +/**** Private function definitions ****/ diff --git a/firmware/src/sort/cv_out.h b/firmware/src/sort/cv_out.h new file mode 100644 index 0000000..87fd437 --- /dev/null +++ b/firmware/src/sort/cv_out.h @@ -0,0 +1,41 @@ +#ifndef CONST_VOLTAGE_OUTPUT_H_ +#define CONST_VOLTAGE_OUTPUT_H_ + +/**** Includes ****/ +#include +#include "ain.h" +#include "halfbridge.h" + +namespace bsp { + +/**** Public definitions ****/ + +class CVOut +{ + public: + CVOut(void); + ~CVOut(void); + + void init(AnalogIn* ain_ch, Halfbridge* out_ch); + + uint16_t voltage; + uint8_t disabled; + uint8_t process_ain; + + void process(void); + + #ifndef TESTING + protected: + #endif + AnalogIn* ain_ch; + Halfbridge* out_ch; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/sort/fuse.cpp b/firmware/src/sort/fuse.cpp new file mode 100644 index 0000000..1ed0dd1 --- /dev/null +++ b/firmware/src/sort/fuse.cpp @@ -0,0 +1,81 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "fuse.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +bsp::Fuse::Fuse(void) +{ + return; +} + +bsp::Fuse::~Fuse(void) +{ + return; +} + +void bsp::Fuse::init(bsp::AnalogIn* ain_ch, bsp::VCounter* timerh) +{ + this->ain_ch = ain_ch; + this->timer = timer; + + this->current_lim = 0; + this->hold_time = 0; + this->cooldown_time = 0; + + this->process_ain = 1; + this->auto_reset = 0; + + this->warning = 0; + this->fault = 0; + + this->ts_oc_chnage = 0; +} + +void bsp::Fuse::process(void) +{ + // Update analog input + if(this->process_ain) this->ain_ch->read(); + + // Get current time + uint16_t ts_now = this->timer->read(); + + // Update over current and warning condition + uint8_t is_oc = 0; + if(this->ain_ch->last_read >= this->current_lim) is_oc = 1; + + // Note start time if new OC condition + if(is_oc != this->warning) this->ts_oc_chnage = ts_now; + + // Update warning + this->warning = is_oc; + + // Calculate OC condition time + uint16_t td = util::time_delta(this->ts_oc_chnage, ts_now); + uint32_t time_ms = this->timer->convert_ms(td); + + // Check for fault set + if((this->fault==0)&&(time_ms > (uint32_t)this->hold_time)) + { + this->fault = 1; + return; + }; + + // Check if allowed auto reset + if((this->auto_reset==0)||(this->cooldown_time==0)) return; + + // Check for fault reset + if((this->fault!=0)&&(time_ms > (uint32_t)this->cooldown_time)) + { + this->fault = 0; + return; + }; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/sort/fuse.h b/firmware/src/sort/fuse.h new file mode 100644 index 0000000..23189b6 --- /dev/null +++ b/firmware/src/sort/fuse.h @@ -0,0 +1,48 @@ +#ifndef FUSE_H_ +#define FUSE_H_ + +/**** Includes ****/ +#include +#include "ain.h" +#include "vcounter.h" + +namespace bsp { + +/**** Public definitions ****/ + +class Fuse +{ + public: + Fuse(void); + ~Fuse(void); + + void init(AnalogIn* ain_ch, VCounter* timer); + + uint8_t warning; + uint8_t fault; + + uint16_t current_lim; + uint16_t hold_time; + uint16_t cooldown_time; + + uint8_t process_ain; + uint8_t auto_reset; + + void process(void); + + #ifndef TESTING + protected: + #endif + AnalogIn* ain_ch; + VCounter* timer; + uint16_t ts_oc_chnage; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* POTENTIOMETER_H_ */ \ No newline at end of file diff --git a/firmware/src/sort/voltlock.cpp b/firmware/src/sort/voltlock.cpp new file mode 100644 index 0000000..be25a93 --- /dev/null +++ b/firmware/src/sort/voltlock.cpp @@ -0,0 +1,83 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "voltlock.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +bsp::VoltLock::VoltLock(void) +{ + return; +} + +bsp::VoltLock::~VoltLock(void) +{ + return; +} + +void bsp::VoltLock::init(bsp::AnalogIn* ain_ch, bsp::VCounter* timerh) +{ + this->ain_ch = ain_ch; + this->timer = timer; + + this->undervoltage_lim = 0xFFFF; + this->overvoltage_lim = 0; + this->hold_time = 0; + this->cooldown_time = 0; + + this->process_ain = 1; + this->auto_reset = 1; + + this->warning = 0; + this->fault = 0; + + this->ts_oc_chnage = 0; +} + +void bsp::VoltLock::process(void) +{ + // Update analog input + if(this->process_ain) this->ain_ch->read(); + + // Get current time + uint16_t ts_now = this->timer->read(); + + // Update over current and warning condition + uint8_t is_outside = 0; + if(this->ain_ch->last_read <= this->undervoltage_lim) is_outside = 1; + if(this->ain_ch->last_read >= this->overvoltage_lim) is_outside = 1; + + // Note start time if new OC condition + if(is_outside != this->warning) this->ts_oc_chnage = ts_now; + + // Update warning + this->warning = is_outside; + + // Calculate warning condition time + uint16_t td = util::time_delta(this->ts_oc_chnage, ts_now); + uint32_t time_ms = this->timer->convert_ms(td); + + // Check for fault set + if((this->fault==0)&&(time_ms > (uint32_t)this->hold_time)) + { + this->fault = 1; + return; + }; + + // Check if allowed auto reset + if(this->auto_reset==0) return; + + // Check for fault reset + if((this->fault!=0)&&(time_ms > (uint32_t)this->cooldown_time)) + { + this->fault = 0; + return; + }; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/sort/voltlock.h b/firmware/src/sort/voltlock.h new file mode 100644 index 0000000..f22f941 --- /dev/null +++ b/firmware/src/sort/voltlock.h @@ -0,0 +1,49 @@ +#ifndef VOLTAGE_LOCKOUT_H_ +#define VOLTAGE_LOCKOUT_H_ + +/**** Includes ****/ +#include +#include "ain.h" +#include "vcounter.h" + +namespace bsp { + +/**** Public definitions ****/ + +class VoltLock +{ + public: + VoltLock(void); + ~VoltLock(void); + + void init(AnalogIn* ain_ch, VCounter* timer); + + uint8_t warning; + uint8_t fault; + + uint16_t undervoltage_lim; + uint16_t overvoltage_lim; + uint16_t hold_time; + uint16_t cooldown_time; + + uint8_t process_ain; + uint8_t auto_reset; + + void process(void); + + #ifndef TESTING + protected: + #endif + AnalogIn* ain_ch; + VCounter* timer; + uint16_t ts_oc_chnage; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* VOLTAGE_LOCKOUT_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index f730fb9..64e52e2 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -153,12 +153,78 @@ + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + compile compile + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + compile @@ -174,10 +240,17 @@ compile + + compile + + + compile + + diff --git a/firmware/src/utils/utils.cpp b/firmware/src/utils/utils.cpp index b4801bc..ad9e02c 100644 --- a/firmware/src/utils/utils.cpp +++ b/firmware/src/utils/utils.cpp @@ -17,6 +17,18 @@ uint8_t util::invert(uint8_t x) else return 1; } +uint16_t util::invert(uint16_t x) +{ + if(x!=0) return 0; + else return 1; +} + +uint32_t util::invert(uint32_t x) +{ + if(x!=0) return 0; + else return 1; +} + uint8_t util::sat_add(uint8_t x, uint8_t y) { uint8_t z = x + y; @@ -97,6 +109,46 @@ uint16_t util::sat_cast(int32_t x) else return (uint16_t)x; } +uint8_t util::is_timed_out(uint16_t time, uint16_t limit) +{ + if(time >= limit) return 1; + else return 0; +} + +uint8_t util::is_in_range(uint16_t value, uint16_t min, uint16_t max) +{ + if((value >= min)&&(value <= max)) return 1; + else return 0; +} + +uint16_t util::time_delta(uint16_t start, uint16_t end) +{ + if(end >= start) return (end-start); + uint16_t temp = 0xFFFF - start; + return temp + end; +} + +uint32_t util::time_delta(uint32_t start, uint32_t end) +{ + if(end >= start) return (end-start); + uint32_t temp = 0xFFFFFFFF - start; + return temp + end; +} + +uint16_t util::time_delta(uint16_t start, uint16_t end, uint16_t max) +{ + if(end >= start) return (end-start); + uint16_t temp = max - start; + return temp + end; +} + +uint32_t util::time_delta(uint32_t start, uint32_t end, uint32_t max) +{ + if(end >= start) return (end-start); + uint32_t temp = max - start; + return temp + end; +} + uint16_t util::convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset) { int32_t temp = (int32_t)raw; diff --git a/firmware/src/utils/utils.h b/firmware/src/utils/utils.h index 9f16bb9..12d6687 100644 --- a/firmware/src/utils/utils.h +++ b/firmware/src/utils/utils.h @@ -9,17 +9,8 @@ namespace util { /**** Public definitions ****/ /**** Public function declarations ****/ uint8_t invert(uint8_t x); - -uint16_t sat_cast(uint32_t x); -uint16_t sat_cast(int32_t x); - -uint16_t convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset); -uint16_t sat_mul_kilo(uint16_t xk, uint16_t yk); -uint16_t sat_div_kilo(uint16_t top, uint16_t bot); -uint16_t sat_ratio(uint16_t top, uint16_t bot); -uint16_t percent_to_16b(uint8_t percent); - -uint16_t percent_of(uint8_t percent, uint16_t value); +uint16_t invert(uint16_t x); +uint32_t invert(uint32_t x); uint8_t sat_add(uint8_t x, uint8_t y); uint16_t sat_add(uint16_t x, uint16_t y); @@ -33,8 +24,25 @@ uint8_t abs_subtract(uint8_t x, uint8_t y); uint16_t abs_subtract(uint16_t x, uint16_t y); uint32_t abs_subtract(uint32_t x, uint32_t y); -uint16_t interpolate_1d(uint16_t x, uint16_t* x_axis, uint16_t* y_values, uint8_t len_axis); -uint16_t interpolate_2d(uint16_t x, uint16_t y, uint16_t* x_axis, uint8_t len_x_axis, uint16_t* y_axis, uint8_t len_y_axis, uint16_t* z_values); +uint16_t sat_cast(uint32_t x); +uint16_t sat_cast(int32_t x); + +uint8_t is_timed_out(uint16_t time, uint16_t limit); +uint8_t is_in_range(uint16_t value, uint16_t min, uint16_t max); + +uint16_t time_delta(uint16_t start, uint16_t end); +uint32_t time_delta(uint32_t start, uint32_t end); + +uint16_t time_delta(uint16_t start, uint16_t end, uint16_t max); +uint32_t time_delta(uint32_t start, uint32_t end, uint32_t max); + +uint16_t convert_muldivoff(uint16_t raw, uint8_t mul, uint8_t div, int16_t offset); +uint16_t sat_mul_kilo(uint16_t xk, uint16_t yk); +uint16_t sat_div_kilo(uint16_t top, uint16_t bot); +uint16_t sat_ratio(uint16_t top, uint16_t bot); +uint16_t percent_to_16b(uint8_t percent); + +uint16_t percent_of(uint8_t percent, uint16_t value); #ifdef TESTING #endif diff --git a/firmware/src/utils/vcounter.cpp b/firmware/src/utils/vcounter.cpp new file mode 100644 index 0000000..6870cc1 --- /dev/null +++ b/firmware/src/utils/vcounter.cpp @@ -0,0 +1,67 @@ +/**** Includes ****/ +#include "utils.h" +#include "vcounter.h" + +using namespace util; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +util::VCounter::VCounter(void) +{ + return; +} + +util::VCounter::~VCounter(void) +{ + return; +} + +void util::VCounter::init(uint16_t top, uint16_t step_us) +{ + this->counter = 0; + this->top = top; + this->step_us = step_us; + this->disabled = 1; +} + +void util::VCounter::reset(void) +{ + this->counter = 0; +} + +void util::VCounter::increment(void) +{ + if(this->disabled) return; + this->counter++; + if(this->counter > this->top) this->counter = 0; +} + +uint16_t util::VCounter::read(void) +{ + return this->counter; +} + +uint32_t util::VCounter::read_ms(void) +{ + return this->convert_ms(this->counter); +} + +uint16_t util::VCounter::read_top(void) +{ + return this->top; +} + +uint32_t util::VCounter::convert_ms(uint16_t raw) +{ + if(this->step_us==0) return 0; + + uint32_t out = (uint32_t)raw * (uint32_t)this->step_us; + return out/1000; +} + + +/**** Private function definitions ****/ + diff --git a/firmware/src/utils/vcounter.h b/firmware/src/utils/vcounter.h new file mode 100644 index 0000000..557bac8 --- /dev/null +++ b/firmware/src/utils/vcounter.h @@ -0,0 +1,42 @@ +#ifndef VIRTUAL_COUNTER_H_ +#define VIRTUAL_COUNTER_H_ + +/**** Includes ****/ +#include + +namespace util { + +/**** Public definitions ****/ +class VCounter +{ + public: + VCounter(void); + ~VCounter(void); + + void init(uint16_t top, uint16_t step_us); + + uint8_t disabled; + + void reset(void); + void increment(void); + uint16_t read(void); + uint32_t read_ms(void); + uint16_t read_top(void); + uint32_t convert_ms(uint16_t raw); + + #ifndef TESTING + protected: + #endif + uint16_t step_us; + uint16_t counter; + uint16_t top; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* VIRTUAL_COUNTER_H_ */ \ No newline at end of file -- 2.49.1 From 1c560a9b0fa4aa290aa8cc46887ace6b54244cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Thu, 25 Jul 2024 23:25:59 +0300 Subject: [PATCH 33/35] Saved work --- firmware/src/bsp/board.cpp | 8 +- firmware/src/bsp/board.h | 9 ++- firmware/src/bsp/memory.cpp | 55 +++++++++++++ firmware/src/bsp/memory.h | 36 +++++++++ firmware/src/{hw => dccd}/dccd_hw.cpp | 62 ++++++++++---- firmware/src/{hw => dccd}/dccd_hw.h | 40 ++++----- .../{sort/voltlock.cpp => hw/safe_ain.cpp} | 36 +++++---- firmware/src/hw/safe_ain.h | 50 ++++++++++++ firmware/src/main.cpp | 6 +- firmware/src/sort/fuse.cpp | 81 ------------------- firmware/src/sort/fuse.h | 48 ----------- firmware/src/sort/voltlock.h | 49 ----------- firmware/src/uDCCD.cppproj | 25 ++++-- 13 files changed, 265 insertions(+), 240 deletions(-) create mode 100644 firmware/src/bsp/memory.cpp create mode 100644 firmware/src/bsp/memory.h rename firmware/src/{hw => dccd}/dccd_hw.cpp (55%) rename firmware/src/{hw => dccd}/dccd_hw.h (53%) rename firmware/src/{sort/voltlock.cpp => hw/safe_ain.cpp} (61%) create mode 100644 firmware/src/hw/safe_ain.h delete mode 100644 firmware/src/sort/fuse.cpp delete mode 100644 firmware/src/sort/fuse.h delete mode 100644 firmware/src/sort/voltlock.h diff --git a/firmware/src/bsp/board.cpp b/firmware/src/bsp/board.cpp index 6cff7da..7440429 100644 --- a/firmware/src/bsp/board.cpp +++ b/firmware/src/bsp/board.cpp @@ -20,15 +20,17 @@ bsp::Board::~Board(void) return; } -void bsp::Board::init(void) +void bsp::Board::init(boardCfg_t* cfg) { + // Calculate settings + // Controller setup mcu::startupCfg_t mcu_cfg; mcu_cfg.adc_clk = mcu::ADC_DIV64; // 8MHz/64=125kHz mcu_cfg.pwm_clk = mcu::TIM_DIV1; // 8MHz/1 = 8MHz - mcu_cfg.pwm_top = 500; // 8000kHz/500 = 16kHz - mcu_cfg.od_common_is_pwm = 1; // Open-drain common is PWM + mcu_cfg.pwm_top = 4000/(uint16_t)cfg->pwm_f_khz; + mcu_cfg.od_common_is_pwm = cfg->od_common_is_pwm; mcu::startup(&mcu_cfg); diff --git a/firmware/src/bsp/board.h b/firmware/src/bsp/board.h index 665109c..bce363e 100644 --- a/firmware/src/bsp/board.h +++ b/firmware/src/bsp/board.h @@ -16,10 +16,15 @@ namespace bsp { class Board { public: + typedef struct { + uint8_t pwm_f_khz; + uint8_t od_common_is_pwm; + } boardCfg_t; + Board(void); ~Board(void); - void init(void); + void init(boardCfg_t* cfg); AnalogIn out_voltage; AnalogIn out_current; @@ -50,6 +55,8 @@ class Board PwmOut out_pwm; DigitalOut out_low; + Memory nvmem; + void read(void); #ifndef TESTING diff --git a/firmware/src/bsp/memory.cpp b/firmware/src/bsp/memory.cpp new file mode 100644 index 0000000..a186eae --- /dev/null +++ b/firmware/src/bsp/memory.cpp @@ -0,0 +1,55 @@ +/**** Includes ****/ +#include "mcu/mcu_hal.h" +#include "memory.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +/**** Public function definitions ****/ +bsp::Memory::Memory(void) +{ + return; +} + +bsp::Memory::~Memory(void) +{ + return; +} + +uint8_t bsp::Memory::read_8b(uint16_t address) +{ + return mcu::eeprom_read8b(address); +} + + +uint16_t bsp::Memory::read_16b(uint16_t address) +{ + return mcu::eeprom_read16b(address); +} + +uint32_t bsp::Memory::read_32b(uint16_t address) +{ + return mcu::eeprom_read32b(address); +} + +void bsp::Memory::write_8b(uint16_t address, uint8_t value) +{ + mcu::eeprom_write8b(address, value); +} + + +void bsp::Memory::write_16b(uint16_t address, uint16_t value) +{ + mcu::eeprom_write16b(address, value); +} + +void bsp::Memory::write_32b(uint16_t address, uint32_t value) +{ + mcu::eeprom_write32b(address, value); +} + +/**** Private function definitions ****/ + diff --git a/firmware/src/bsp/memory.h b/firmware/src/bsp/memory.h new file mode 100644 index 0000000..141d4ee --- /dev/null +++ b/firmware/src/bsp/memory.h @@ -0,0 +1,36 @@ +#ifndef DIGITAL_IN_H_ +#define DIGITAL_IN_H_ + +/**** Includes ****/ +#include + +namespace bsp { + +/**** Public definitions ****/ +class Memory +{ + public: + Memory(void); + ~Memory(void); + + uint8_t read_8b(uint16_t address); + uint16_t read_16b(uint16_t address); + uint32_t read_32b(uint16_t address); + + void write_8b(uint16_t address, uint8_t value); + void write_16b(uint16_t address, uint16_t value); + void write_32b(uint16_t address, uint32_t value); + + #ifndef TESTING + protected: + #endif +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DIGITAL_IN_H_ */ \ No newline at end of file diff --git a/firmware/src/hw/dccd_hw.cpp b/firmware/src/dccd/dccd_hw.cpp similarity index 55% rename from firmware/src/hw/dccd_hw.cpp rename to firmware/src/dccd/dccd_hw.cpp index fef1c58..4ebc45d 100644 --- a/firmware/src/hw/dccd_hw.cpp +++ b/firmware/src/dccd/dccd_hw.cpp @@ -1,6 +1,5 @@ /**** Includes ****/ #include "../utils/utils.h" -#include "../bsp/mcu/mcu_hal.h" #include "dccd_hw.h" using namespace hw; @@ -10,26 +9,59 @@ using namespace hw; /**** Private variables ****/ /**** Private function declarations ****/ /**** Public function definitions ****/ -hw::DccdHw::DccdHw(void) +dccd::DccdHw::DccdHw(void) { return; } -hw::DccdHw::~DccdHw(void) +dccd::DccdHw::~DccdHw(void) { return; } -void hw::DccdHw::init(dccdHwCfg_t* cfg) +void dccd::DccdHw::init(dccdHwCfg_t* cfg) { - this->board_hw.init(); + bsp::Board::boardCfg_t board_cfg; + + board_cfg.pwm_f_khz = cfg->pwm_f_khz; + board_cfg.od_common_is_pwm = 1; + + this->board_hw.init(&board_cfg); this->counter.init(0xFFFF, 900); - this->out_voltage = 0; - this->out_current = 0; - this->battery_voltage = 12000; - this->battery_current = 0; + this->out_voltage.init(&(this->board_hw.out_voltage), &(this->counter)); + this->out_voltage.under_treshold = 0; + this->out_voltage.over_treshold = 9000; + this->out_voltage.hold_time = 1000; + this->out_voltage.cooldown_time = 0; + this->out_voltage.update_ain = 0; + this->out_voltage.auto_reset = 1; + + this->out_current.init(&(this->board_hw.out_current), &(this->counter)); + this->out_current.under_treshold = 0; + this->out_current.over_treshold = 6000; + this->out_current.hold_time = 200; + this->out_current.cooldown_time = 1000; + this->out_current.update_ain = 0; + this->out_current.auto_reset = 1; + + this->battery_voltage.init(&(this->board_hw.battery_voltage), &(this->counter)); + this->battery_voltage.under_treshold = 9000; + this->battery_voltage.over_treshold = 18000; + this->battery_voltage.hold_time = 1000; + this->battery_voltage.cooldown_time = 0; + this->battery_voltage.update_ain = 0; + this->battery_voltage.auto_reset = 1; + this->battery_voltage.last_read = 12000; + + this->battery_current.init(&(this->board_hw.battery_current), &(this->counter)); + this->battery_current.under_treshold = 0; + this->battery_current.over_treshold = 8000; + this->battery_current.hold_time = 200; + this->battery_current.cooldown_time = 1000; + this->battery_current.update_ain = 0; + this->battery_current.auto_reset = 1; this->btn_up.init(&(this->board_hw.din4), 0, &(this->counter), 10); this->btn_up.update_din = 0; @@ -54,7 +86,7 @@ void hw::DccdHw::init(dccdHwCfg_t* cfg) this->outdriver.init(&(this->board_hw.out_pwm), &(this->board_hw.out_low)); - LedDisplay::doutCfg_t dsp_cfg; + hw::LedDisplay::doutCfg_t dsp_cfg; dsp_cfg.led0_dout_ch = &(this->board_hw.od1); dsp_cfg.led1_dout_ch = &(this->board_hw.od2); dsp_cfg.led2_dout_ch = &(this->board_hw.od3); @@ -85,17 +117,17 @@ void hw::DccdHw::init(dccdHwCfg_t* cfg) this->display.write(0x00); } -void hw::DccdHw::read(void) +void dccd::DccdHw::read(void) { // Update low level inputs this->board_hw.read(); this->counter.increment(); - this->out_voltage = this->board_hw.out_voltage.last_read; - this->out_current = this->board_hw.out_current.last_read; - this->battery_voltage = this->board_hw.battery_voltage.last_read; - this->battery_current = this->board_hw.battery_current.last_read; + this->out_voltage.process(); + this->out_current.process(); + this->battery_voltage.process(); + this->battery_current.process(); this->btn_up.process(); this->btn_down.process(); diff --git a/firmware/src/hw/dccd_hw.h b/firmware/src/dccd/dccd_hw.h similarity index 53% rename from firmware/src/hw/dccd_hw.h rename to firmware/src/dccd/dccd_hw.h index 2a96053..a4d864c 100644 --- a/firmware/src/hw/dccd_hw.h +++ b/firmware/src/dccd/dccd_hw.h @@ -5,18 +5,20 @@ #include #include "../bsp/board.h" #include "../utils/vcounter.h" -#include "button.h" -#include "led_display.h" -#include "potentiometer.h" -#include "out_driver.h" +#include "../hw/button.h" +#include "../hw/led_display.h" +#include "../hw/potentiometer.h" +#include "../hw/out_driver.h" +#include "../hw/safe_ain.h" -namespace hw { +namespace dccd { /**** Public definitions ****/ class DccdHw { public: typedef struct { + uint8_t pwm_f_khz; uint8_t handbrake_pull_up; uint8_t speed_hall; } dccdHwCfg_t; @@ -27,23 +29,25 @@ class DccdHw void init(dccdHwCfg_t* cfg); // Inputs - uint16_t out_voltage; - uint16_t out_current; - uint16_t battery_voltage; - uint16_t battery_current; + hw::SafeAin out_voltage; + hw::SafeAin out_current; + hw::SafeAin battery_voltage; + hw::SafeAin battery_current; - Button btn_up; - Button btn_down; - Button btn_mode; - Button handbrake; - Button brakes; - Button dimm; + hw::Button btn_up; + hw::Button btn_down; + hw::Button btn_mode; + hw::Button handbrake; + hw::Button brakes; + hw::Button dimm; - Potentiometer pot; + hw::Potentiometer pot; // Outputs - LedDisplay display; - OutDriver outdriver; + hw::LedDisplay display; + hw::OutDriver outdriver; + + // Safety void read(void); diff --git a/firmware/src/sort/voltlock.cpp b/firmware/src/hw/safe_ain.cpp similarity index 61% rename from firmware/src/sort/voltlock.cpp rename to firmware/src/hw/safe_ain.cpp index be25a93..5cbc54e 100644 --- a/firmware/src/sort/voltlock.cpp +++ b/firmware/src/hw/safe_ain.cpp @@ -1,8 +1,8 @@ /**** Includes ****/ #include "../utils/utils.h" -#include "voltlock.h" +#include "safe_ain.h" -using namespace bsp; +using namespace hw; /**** Private definitions ****/ /**** Private constants ****/ @@ -10,56 +10,60 @@ using namespace bsp; /**** Private function declarations ****/ /**** Public function definitions ****/ -bsp::VoltLock::VoltLock(void) +hw::SafeAin::SafeAin(void) { return; } -bsp::VoltLock::~VoltLock(void) +hw::SafeAin::~SafeAin(void) { return; } -void bsp::VoltLock::init(bsp::AnalogIn* ain_ch, bsp::VCounter* timerh) +void hw::SafeAin::init(bsp::AnalogIn* ain_ch, util::VCounter* timer) { this->ain_ch = ain_ch; this->timer = timer; - this->undervoltage_lim = 0xFFFF; - this->overvoltage_lim = 0; + this->under_treshold = 0; + this->over_treshold = 0xFFFF; this->hold_time = 0; this->cooldown_time = 0; - this->process_ain = 1; - this->auto_reset = 1; + this->update_ain = 0; + this->auto_reset = 0; this->warning = 0; this->fault = 0; - this->ts_oc_chnage = 0; + this->last_read = 0; + + this->ts_state_chnage = 0; } -void bsp::VoltLock::process(void) +void hw::SafeAin::process(void) { // Update analog input - if(this->process_ain) this->ain_ch->read(); + if(this->update_ain) this->ain_ch->read(); + + this->last_read = this->ain_ch->last_read; // Get current time uint16_t ts_now = this->timer->read(); // Update over current and warning condition uint8_t is_outside = 0; - if(this->ain_ch->last_read <= this->undervoltage_lim) is_outside = 1; - if(this->ain_ch->last_read >= this->overvoltage_lim) is_outside = 1; + if(this->last_read < this->under_treshold) is_outside = 1; + if(this->last_read > this->over_treshold) is_outside = 1; // Note start time if new OC condition - if(is_outside != this->warning) this->ts_oc_chnage = ts_now; + if(is_outside != this->warning) this->ts_state_chnage = ts_now; // Update warning this->warning = is_outside; // Calculate warning condition time - uint16_t td = util::time_delta(this->ts_oc_chnage, ts_now); + uint16_t td = util::time_delta(this->ts_state_chnage, ts_now); uint32_t time_ms = this->timer->convert_ms(td); // Check for fault set diff --git a/firmware/src/hw/safe_ain.h b/firmware/src/hw/safe_ain.h new file mode 100644 index 0000000..a48969a --- /dev/null +++ b/firmware/src/hw/safe_ain.h @@ -0,0 +1,50 @@ +#ifndef SAFE_AIN_H_ +#define SAFE_AIN_H_ + +/**** Includes ****/ +#include +#include "../utils/vcounter.h" +#include "../bsp/board.h" + +namespace hw { + +/**** Public definitions ****/ + +class SafeAin +{ + public: + SafeAin(void); + ~SafeAin(void); + + void init(bsp::AnalogIn* ain_ch, util::VCounter* timer); + + uint8_t warning; + uint8_t fault; + uint16_t last_read; + + uint16_t under_treshold; + uint16_t over_treshold; + uint16_t hold_time; + uint16_t cooldown_time; + + uint8_t update_ain; + uint8_t auto_reset; + + void process(void); + + #ifndef TESTING + protected: + #endif + bsp::AnalogIn* ain_ch; + util::VCounter* timer; + uint16_t ts_state_chnage; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* SAFE_AIN_H_ */ \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 9dcfdd8..aba810f 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,19 +1,19 @@ /**** Includes ****/ #include "utils/utils.h" -#include "hw/dccd_hw.h" +#include "dccd/dccd_hw.h" /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ -hw::DccdHw dccd_hw; +dccd::DccdHw dccd_hw; /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) { // Setup - hw::DccdHw::dccdHwCfg_t hw_cfg; + dccd::DccdHw::dccdHwCfg_t hw_cfg; hw_cfg.handbrake_pull_up = 0; hw_cfg.speed_hall = 0; diff --git a/firmware/src/sort/fuse.cpp b/firmware/src/sort/fuse.cpp deleted file mode 100644 index 1ed0dd1..0000000 --- a/firmware/src/sort/fuse.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "fuse.h" - -using namespace bsp; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -bsp::Fuse::Fuse(void) -{ - return; -} - -bsp::Fuse::~Fuse(void) -{ - return; -} - -void bsp::Fuse::init(bsp::AnalogIn* ain_ch, bsp::VCounter* timerh) -{ - this->ain_ch = ain_ch; - this->timer = timer; - - this->current_lim = 0; - this->hold_time = 0; - this->cooldown_time = 0; - - this->process_ain = 1; - this->auto_reset = 0; - - this->warning = 0; - this->fault = 0; - - this->ts_oc_chnage = 0; -} - -void bsp::Fuse::process(void) -{ - // Update analog input - if(this->process_ain) this->ain_ch->read(); - - // Get current time - uint16_t ts_now = this->timer->read(); - - // Update over current and warning condition - uint8_t is_oc = 0; - if(this->ain_ch->last_read >= this->current_lim) is_oc = 1; - - // Note start time if new OC condition - if(is_oc != this->warning) this->ts_oc_chnage = ts_now; - - // Update warning - this->warning = is_oc; - - // Calculate OC condition time - uint16_t td = util::time_delta(this->ts_oc_chnage, ts_now); - uint32_t time_ms = this->timer->convert_ms(td); - - // Check for fault set - if((this->fault==0)&&(time_ms > (uint32_t)this->hold_time)) - { - this->fault = 1; - return; - }; - - // Check if allowed auto reset - if((this->auto_reset==0)||(this->cooldown_time==0)) return; - - // Check for fault reset - if((this->fault!=0)&&(time_ms > (uint32_t)this->cooldown_time)) - { - this->fault = 0; - return; - }; -} - -/**** Private function definitions ****/ diff --git a/firmware/src/sort/fuse.h b/firmware/src/sort/fuse.h deleted file mode 100644 index 23189b6..0000000 --- a/firmware/src/sort/fuse.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef FUSE_H_ -#define FUSE_H_ - -/**** Includes ****/ -#include -#include "ain.h" -#include "vcounter.h" - -namespace bsp { - -/**** Public definitions ****/ - -class Fuse -{ - public: - Fuse(void); - ~Fuse(void); - - void init(AnalogIn* ain_ch, VCounter* timer); - - uint8_t warning; - uint8_t fault; - - uint16_t current_lim; - uint16_t hold_time; - uint16_t cooldown_time; - - uint8_t process_ain; - uint8_t auto_reset; - - void process(void); - - #ifndef TESTING - protected: - #endif - AnalogIn* ain_ch; - VCounter* timer; - uint16_t ts_oc_chnage; -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* POTENTIOMETER_H_ */ \ No newline at end of file diff --git a/firmware/src/sort/voltlock.h b/firmware/src/sort/voltlock.h deleted file mode 100644 index f22f941..0000000 --- a/firmware/src/sort/voltlock.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef VOLTAGE_LOCKOUT_H_ -#define VOLTAGE_LOCKOUT_H_ - -/**** Includes ****/ -#include -#include "ain.h" -#include "vcounter.h" - -namespace bsp { - -/**** Public definitions ****/ - -class VoltLock -{ - public: - VoltLock(void); - ~VoltLock(void); - - void init(AnalogIn* ain_ch, VCounter* timer); - - uint8_t warning; - uint8_t fault; - - uint16_t undervoltage_lim; - uint16_t overvoltage_lim; - uint16_t hold_time; - uint16_t cooldown_time; - - uint8_t process_ain; - uint8_t auto_reset; - - void process(void); - - #ifndef TESTING - protected: - #endif - AnalogIn* ain_ch; - VCounter* timer; - uint16_t ts_oc_chnage; -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* VOLTAGE_LOCKOUT_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 64e52e2..5d31aad 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -183,6 +183,12 @@ compile + + compile + + + compile + compile @@ -195,24 +201,30 @@ compile + + compile + + + compile + compile compile - - compile - - - compile - compile compile + + compile + + + compile + compile @@ -251,6 +263,7 @@ + -- 2.49.1 From 196d440b2b945dcdc2ca7286746c06bfb8a54d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Fri, 26 Jul 2024 21:27:39 +0300 Subject: [PATCH 34/35] Could be functioning --- firmware/src/bsp/board.h | 1 + firmware/src/bsp/memory.h | 6 +- firmware/src/dccd/dccd.cpp | 257 ++++++++++++++++++++++++++++++++++ firmware/src/dccd/dccd.h | 44 ++++++ firmware/src/dccd/dccd_hw.cpp | 27 +++- firmware/src/dccd/dccd_hw.h | 6 +- firmware/src/hw/out_reg.cpp | 103 ++++++++++++++ firmware/src/hw/out_reg.h | 56 ++++++++ firmware/src/main.cpp | 24 ++-- firmware/src/sort/cv_out.cpp | 50 ------- firmware/src/sort/cv_out.h | 41 ------ firmware/src/uDCCD.cppproj | 16 ++- 12 files changed, 516 insertions(+), 115 deletions(-) create mode 100644 firmware/src/dccd/dccd.cpp create mode 100644 firmware/src/dccd/dccd.h create mode 100644 firmware/src/hw/out_reg.cpp create mode 100644 firmware/src/hw/out_reg.h delete mode 100644 firmware/src/sort/cv_out.cpp delete mode 100644 firmware/src/sort/cv_out.h diff --git a/firmware/src/bsp/board.h b/firmware/src/bsp/board.h index bce363e..97a5d9d 100644 --- a/firmware/src/bsp/board.h +++ b/firmware/src/bsp/board.h @@ -9,6 +9,7 @@ #include "din.h" #include "dout.h" #include "pwm_out.h" +#include "memory.h" namespace bsp { diff --git a/firmware/src/bsp/memory.h b/firmware/src/bsp/memory.h index 141d4ee..b66c293 100644 --- a/firmware/src/bsp/memory.h +++ b/firmware/src/bsp/memory.h @@ -1,5 +1,5 @@ -#ifndef DIGITAL_IN_H_ -#define DIGITAL_IN_H_ +#ifndef MEMORY_IN_H_ +#define MEMORY_IN_H_ /**** Includes ****/ #include @@ -33,4 +33,4 @@ class Memory } //namespace -#endif /* DIGITAL_IN_H_ */ \ No newline at end of file +#endif /* MEMORY_IN_H_ */ \ No newline at end of file diff --git a/firmware/src/dccd/dccd.cpp b/firmware/src/dccd/dccd.cpp new file mode 100644 index 0000000..a77e39f --- /dev/null +++ b/firmware/src/dccd/dccd.cpp @@ -0,0 +1,257 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "dccd.h" + +using namespace dccd; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ +static uint8_t img_gen_dot10(uint8_t percent); +static uint8_t img_gen_dot20(uint8_t percent); +static uint8_t img_gen_bar(uint8_t percent); + +/**** Public function definitions ****/ +dccd::DccdApp::DccdApp(void) +{ + return; +} + +dccd::DccdApp::~DccdApp(void) +{ + return; +} + +void dccd::DccdApp::init(void) +{ + // Read settings from EEPROM + + DccdHw::dccdHwCfg_t cfg; + cfg.handbrake_pull_up = 1; + cfg.pwm_f_khz = 16; + cfg.speed_hall = 0; + + this->hardware.init(&cfg); + + this->lock_current = 4500; + this->max_hbrake_time = 1500; + this->btn_repeat_time = 500; + this->button_inputs = 1; + + this->display_brigth = 100; + this->display_dimm = 50; + + this->btn_force = 0; + this->pot_force = 0; + this->brake_mode = 0; + + this->hardware.read(); + + this->hardware.outreg.voltage = 0; + this->hardware.outreg.current = 0; + this->hardware.outreg.out_on = 1; + this->hardware.display.write(0x01); + this->hardware.display.write_backlight(50); + + this->hardware.write(); +} + +void dccd::DccdApp::process(void) +{ + // Update all inputs + this->hardware.read(); + + uint8_t is_new_mode = 0; + uint8_t is_new_btn_force = 0; + + // Process mode button + if((this->hardware.btn_mode.state==1)&&((this->hardware.btn_mode.is_new)||(this->hardware.btn_mode.time_read() >= this->btn_repeat_time))) + { + this->hardware.btn_mode.time_reset(); + this->hardware.btn_mode.is_new = 0; + // Change mode + switch(this->brake_mode) + { + case 0: + this->brake_mode = 1; + break; + + case 1: + this->brake_mode = 2; + break; + + default: + this->brake_mode = 0; + break; + } + is_new_mode = 1; + }; + + // Process user force inputs + if((this->hardware.btn_up.state==1)&&((this->hardware.btn_up.is_new)||(this->hardware.btn_up.time_read() >= this->btn_repeat_time))) + { + this->hardware.btn_up.time_reset(); + this->hardware.btn_up.is_new = 0; + // Increase user force + this->btn_force += 10; + if(this->btn_force > 100) this->btn_force = 100; + is_new_btn_force = 1; + }; + + if((this->hardware.btn_down.state==1)&&((this->hardware.btn_down.is_new)||(this->hardware.btn_down.time_read() >= this->btn_repeat_time))) + { + this->hardware.btn_down.time_reset(); + this->hardware.btn_down.is_new = 0; + // Decrease user force + this->btn_force -= 10; + if(this->btn_force > 100) this->btn_force = 0; + is_new_btn_force = 1; + }; + + this->pot_force = this->hardware.pot.last_percent; + + // Determine user force + int8_t user_force; + if(this->button_inputs) user_force = (int8_t)this->btn_force; + else user_force = (int8_t)this->pot_force; + + // Determine next settable force + int8_t next_force; + + if((this->hardware.handbrake.state == 1)&&(this->hardware.handbrake.time_read() < this->max_hbrake_time)) + { + // Handbrake override + next_force = -1; + } + else if(this->hardware.brakes.state == 1) + { + // Brakes override + switch(this->brake_mode) + { + case 0: + next_force = -1; + break; + + case 1: + next_force = user_force; + break; + + case 2: + next_force = 100; + break; + + default: + this->brake_mode = -1; + break; + } + } + else + { + // User force + next_force = user_force; + } + + // Apply next force + if(next_force < 0) + { + // HiZ + this->hardware.outreg.voltage = 0; + this->hardware.outreg.current = 0; + this->hardware.outreg.out_on = 0; + } + else if(next_force == 0) + { + // Open + this->hardware.outreg.voltage = 0; + this->hardware.outreg.current = 0; + this->hardware.outreg.out_on = 1; + } + else + { + // Calculate current and voltage settings + this->hardware.outreg.current = util::percent_of((uint8_t)next_force, this->lock_current); + this->hardware.outreg.voltage = util::sat_mul_kilo(this->hardware.outreg.current, 2500); + this->hardware.outreg.out_on = 1; + } + + // Display image + if(is_new_mode) + { + uint8_t image; + switch(this->brake_mode) + { + case 0: + image = 0x07; + break; + + case 1: + image = 0x1E; + break; + + case 2: + image = 0x38; + break; + + default: + image = 0x07; + break; + } + this->hardware.display.write(image, 1000, 1000, 1); + } + else if(is_new_btn_force) + { + this->hardware.display.write(img_gen_dot10(this->btn_force), 500, 500, 1); + } + else if(this->hardware.display.is_cycle_end()) + { + this->hardware.display.write(img_gen_dot10(next_force)); + }; + + // Display backlight + if(this->hardware.dimm.is_new) + { + this->hardware.dimm.is_new = 0; + if(this->hardware.dimm.state) this->hardware.display.write_backlight(this->display_dimm); + else this->hardware.display.write_backlight(this->display_brigth); + }; + + // Execute outputs + this->hardware.write(); +} + +/**** Private function definitions ***/ +static uint8_t img_gen_dot10(uint8_t percent) +{ + if(percent<6) return 0x01; + else if(percent<16) return 0x03; + else if(percent<26) return 0x02; + else if(percent<36) return 0x06; + else if(percent<46) return 0x04; + else if(percent<56) return 0x0C; + else if(percent<66) return 0x08; + else if(percent<76) return 0x18; + else if(percent<86) return 0x10; + else if(percent<96) return 0x30; + else return 0x20; +} + +static uint8_t img_gen_dot20(uint8_t percent) +{ + if(percent<11) return 0x01; + else if(percent<31) return 0x02; + else if(percent<51) return 0x04; + else if(percent<71) return 0x08; + else if(percent<91) return 0x10; + else return 0x20; +} + +static uint8_t img_gen_bar(uint8_t percent) +{ + if(percent<11) return 0x01; + else if(percent<31) return 0x03; + else if(percent<51) return 0x07; + else if(percent<71) return 0x0F; + else if(percent<91) return 0x1F; + else return 0x3F; +} diff --git a/firmware/src/dccd/dccd.h b/firmware/src/dccd/dccd.h new file mode 100644 index 0000000..a723aa2 --- /dev/null +++ b/firmware/src/dccd/dccd.h @@ -0,0 +1,44 @@ +#ifndef DCCD_APP_H_ +#define DCCD_APP_H_ + +/**** Includes ****/ +#include +#include "dccd_hw.h" + +namespace dccd { + +/**** Public definitions ****/ +class DccdApp +{ + public: + DccdApp(void); + ~DccdApp(void); + + void init(void); + void process(void); + + uint16_t lock_current; + uint16_t max_hbrake_time; + uint16_t btn_repeat_time; + uint8_t button_inputs; + uint8_t display_brigth; + uint8_t display_dimm; + + uint8_t btn_force; + uint8_t pot_force; + uint8_t brake_mode; + + #ifdef TESTING + protected: + #endif + dccd::DccdHw hardware; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* DCCD_APP_H_ */ \ No newline at end of file diff --git a/firmware/src/dccd/dccd_hw.cpp b/firmware/src/dccd/dccd_hw.cpp index 4ebc45d..7bceec4 100644 --- a/firmware/src/dccd/dccd_hw.cpp +++ b/firmware/src/dccd/dccd_hw.cpp @@ -2,7 +2,7 @@ #include "../utils/utils.h" #include "dccd_hw.h" -using namespace hw; +using namespace dccd; /**** Private definitions ****/ /**** Private constants ****/ @@ -22,10 +22,8 @@ dccd::DccdHw::~DccdHw(void) void dccd::DccdHw::init(dccdHwCfg_t* cfg) { bsp::Board::boardCfg_t board_cfg; - board_cfg.pwm_f_khz = cfg->pwm_f_khz; board_cfg.od_common_is_pwm = 1; - this->board_hw.init(&board_cfg); this->counter.init(0xFFFF, 900); @@ -84,7 +82,13 @@ void dccd::DccdHw::init(dccdHwCfg_t* cfg) this->pot.init(&(this->board_hw.ain2), 500, 4500); this->pot.update_ain = 0; - this->outdriver.init(&(this->board_hw.out_pwm), &(this->board_hw.out_low)); + hw::OutReg::outRegCfg_t outreg_cfg; + outreg_cfg.pwm_high = &this->board_hw.out_pwm; + outreg_cfg.dout_low = &this->board_hw.out_low; + outreg_cfg.ubat = &this->board_hw.battery_voltage; + outreg_cfg.uout = &this->board_hw.out_voltage; + outreg_cfg.iout = &this->board_hw.out_current; + this->outreg.init(&outreg_cfg); hw::LedDisplay::doutCfg_t dsp_cfg; dsp_cfg.led0_dout_ch = &(this->board_hw.od1); @@ -110,8 +114,13 @@ void dccd::DccdHw::init(dccdHwCfg_t* cfg) else this->board_hw.freq_pull.write(0); // Set initial output states - this->outdriver.write((uint16_t)0); - this->outdriver.enable(); + this->outreg.voltage = 0; + this->outreg.current = 0; + this->outreg.out_on = 0; + this->outreg.lock = 0; + this->outreg.cc_mode_en = 0; + this->outreg.update_ain = 0; + this->outreg.process(); this->display.write_backlight(100); this->display.write(0x00); @@ -139,4 +148,10 @@ void dccd::DccdHw::read(void) this->pot.read(); } +void dccd::DccdHw::write(void) +{ + this->display.process(); + this->outreg.process(); +} + /**** Private function definitions ***/ diff --git a/firmware/src/dccd/dccd_hw.h b/firmware/src/dccd/dccd_hw.h index a4d864c..6ed8897 100644 --- a/firmware/src/dccd/dccd_hw.h +++ b/firmware/src/dccd/dccd_hw.h @@ -10,6 +10,7 @@ #include "../hw/potentiometer.h" #include "../hw/out_driver.h" #include "../hw/safe_ain.h" +#include "../hw/out_reg.h" namespace dccd { @@ -45,11 +46,10 @@ class DccdHw // Outputs hw::LedDisplay display; - hw::OutDriver outdriver; - - // Safety + hw::OutReg outreg; void read(void); + void write(void); #ifdef TESTING protected: diff --git a/firmware/src/hw/out_reg.cpp b/firmware/src/hw/out_reg.cpp new file mode 100644 index 0000000..2e9f8ff --- /dev/null +++ b/firmware/src/hw/out_reg.cpp @@ -0,0 +1,103 @@ +/**** Includes ****/ +#include "../utils/utils.h" +#include "out_reg.h" + +using namespace bsp; + +/**** Private definitions ****/ +/**** Private constants ****/ +/**** Private variables ****/ +/**** Private function declarations ****/ + +/**** Public function definitions ****/ +hw::OutReg::OutReg(void) +{ + return; +} + +hw::OutReg::~OutReg(void) +{ + return; +} + +void hw::OutReg::init(outRegCfg_t* cfg) +{ + this->pwm_high = cfg->pwm_high; + this->dout_low = cfg->dout_low; + this->ubat = cfg->ubat; + this->uout = cfg->uout; + this->iout = cfg->iout; + + this->voltage = 0; + this->current = 0; + this->out_on = 0; + this->lock = 0; + + this->cc_mode_en = 0; + this->update_ain = 0; +} + +void hw::OutReg::process(void) +{ + // Update analog input + if(this->update_ain) + { + this->ubat->read(); + this->uout->read(); + this->iout->read(); + }; + + // Check if turned off + if((out_on == 0)||(this->lock != 0)) + { + + this->pwm_high->write((uint16_t)0); + this->dout_low->write(0); + return; + } + else if(this->dout_low->last_writen == 0) + { + this->dout_low->write(1); + }; + + // Calculate next duty cycle setting + uint16_t next_duty; + + if((this->voltage==0)||(this->current==0)) + { + // Off but not HiZ + next_duty = 0; + } + else if((this->cc_mode_en)&&(this->iout->last_read > this->current)) + { + // Constant current mode + // Reduce voltage to be within current limit + uint32_t temp = (uint32_t)this->pwm_high->get_set_duty() * (uint32_t)this->current; + temp /= this->iout->last_read; + next_duty = util::sat_cast(temp); + } + else + { + // Constant voltage mode + next_duty = util::sat_ratio(this->voltage, this->ubat->last_read); + } + + this->pwm_high->write(next_duty); + + return; +} + +void hw::OutReg::force_off(void) +{ + // Turn off output - HiZ + this->pwm_high->write((uint16_t)0); + this->dout_low->write(0); + + // Update targets + this->voltage = 0; + this->current = 0; + this->out_on = 0; + this->lock = 1; +} + +/**** Private function definitions ****/ diff --git a/firmware/src/hw/out_reg.h b/firmware/src/hw/out_reg.h new file mode 100644 index 0000000..2b93ab1 --- /dev/null +++ b/firmware/src/hw/out_reg.h @@ -0,0 +1,56 @@ +#ifndef OUTPUT_REGULATOR_H_ +#define OUTPUT_REGULATOR_H_ + +/**** Includes ****/ +#include +#include "../bsp/board.h" + +namespace hw { + +/**** Public definitions ****/ + +class OutReg +{ + public: + typedef struct { + bsp::PwmOut* pwm_high; + bsp::DigitalOut* dout_low; + bsp::AnalogIn* ubat; + bsp::AnalogIn* uout; + bsp::AnalogIn* iout; + } outRegCfg_t; + + OutReg(void); + ~OutReg(void); + + void init(outRegCfg_t* cfg); + + uint16_t voltage; + uint16_t current; + uint8_t out_on; + uint8_t lock; + + uint8_t cc_mode_en; + uint8_t update_ain; + + void process(void); + void force_off(void); + + #ifndef TESTING + protected: + #endif + bsp::PwmOut* pwm_high; + bsp::DigitalOut* dout_low; + bsp::AnalogIn* ubat; + bsp::AnalogIn* uout; + bsp::AnalogIn* iout; +}; + +/**** Public function declarations ****/ + +#ifdef TESTING +#endif + +} //namespace + +#endif /* OUTPUT_REGULATOR_H_ */ \ No newline at end of file diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index aba810f..2da7942 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,32 +1,36 @@ /**** Includes ****/ #include "utils/utils.h" -#include "dccd/dccd_hw.h" +#include "dccd/dccd.h" /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ -dccd::DccdHw dccd_hw; /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) { // Setup - dccd::DccdHw::dccdHwCfg_t hw_cfg; + dccd::DccdApp dccd_app; - hw_cfg.handbrake_pull_up = 0; - hw_cfg.speed_hall = 0; + dccd_app.init(); - dccd_hw.init(&hw_cfg); + dccd_app.lock_current = 4500; + dccd_app.max_hbrake_time = 1000; + dccd_app.btn_repeat_time = 500; + dccd_app.button_inputs = 1; + dccd_app.display_brigth = 100; + dccd_app.display_dimm = 50; + + dccd_app.btn_force = 0; + dccd_app.pot_force = 0; + dccd_app.brake_mode = 0; // Super loop while(1) { - // Update inputs - dccd_hw.read(); - - // Do something + dccd_app.process(); continue; // End of super loop } diff --git a/firmware/src/sort/cv_out.cpp b/firmware/src/sort/cv_out.cpp deleted file mode 100644 index 4800e9d..0000000 --- a/firmware/src/sort/cv_out.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/**** Includes ****/ -#include "../utils/utils.h" -#include "cv_out.h" - -using namespace bsp; - -/**** Private definitions ****/ -/**** Private constants ****/ -/**** Private variables ****/ -/**** Private function declarations ****/ - -/**** Public function definitions ****/ -bsp::CVOut::CVOut(void) -{ - return; -} - -bsp::CVOut::~CVOut(void) -{ - return; -} - -void bsp::CVOut::init(AnalogIn* ain_ch, Halfbridge* out_ch) -{ - this->ain_ch = ain_ch; - this->out_ch = out_ch; - - this->voltage = 0; - - this->disabled = 1; - this->process_ain = 1; -} - -void bsp::CVOut::process(void) -{ - // Update analog input - if(this->process_ain) this->ain_ch->read(); - - // Check to HiZ output - if(this->disabled) - { - this->out_ch->write_hiz(); - return; - }; - - // Calculate and apply necessary PWM - this->out_ch->write(util::sat_ratio(this->voltage, this->ain_ch->last_read)); -} - -/**** Private function definitions ****/ diff --git a/firmware/src/sort/cv_out.h b/firmware/src/sort/cv_out.h deleted file mode 100644 index 87fd437..0000000 --- a/firmware/src/sort/cv_out.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CONST_VOLTAGE_OUTPUT_H_ -#define CONST_VOLTAGE_OUTPUT_H_ - -/**** Includes ****/ -#include -#include "ain.h" -#include "halfbridge.h" - -namespace bsp { - -/**** Public definitions ****/ - -class CVOut -{ - public: - CVOut(void); - ~CVOut(void); - - void init(AnalogIn* ain_ch, Halfbridge* out_ch); - - uint16_t voltage; - uint8_t disabled; - uint8_t process_ain; - - void process(void); - - #ifndef TESTING - protected: - #endif - AnalogIn* ain_ch; - Halfbridge* out_ch; -}; - -/**** Public function declarations ****/ - -#ifdef TESTING -#endif - -} //namespace - -#endif /* CONST_VOLTAGE_OUTPUT_H_ */ \ No newline at end of file diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 5d31aad..7919a1a 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -201,6 +201,12 @@ compile + + compile + + + compile + compile @@ -219,10 +225,16 @@ compile - + compile - + + compile + + + compile + + compile -- 2.49.1 From 829d6783ca913696a45653b16c816dcd288a6b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andis=20Z=C4=ABle?= Date: Wed, 31 Jul 2024 19:04:22 +0300 Subject: [PATCH 35/35] Working version No protections though --- firmware/src/dccd/dccd.cpp | 349 ++++++++++++++++++++++++-------- firmware/src/dccd/dccd.h | 10 +- firmware/src/dccd/dccd_hw.cpp | 97 ++++++--- firmware/src/dccd/dccd_hw.h | 1 + firmware/src/hw/led_display.cpp | 7 +- firmware/src/hw/out_reg.cpp | 50 ++++- firmware/src/hw/out_reg.h | 20 +- firmware/src/main.cpp | 46 +++-- firmware/src/uDCCD.cppproj | 16 ++ 9 files changed, 446 insertions(+), 150 deletions(-) diff --git a/firmware/src/dccd/dccd.cpp b/firmware/src/dccd/dccd.cpp index a77e39f..88e7bc2 100644 --- a/firmware/src/dccd/dccd.cpp +++ b/firmware/src/dccd/dccd.cpp @@ -6,6 +6,42 @@ using namespace dccd; /**** Private definitions ****/ /**** Private constants ****/ +static const uint16_t def_lock_current = 4500; +static const uint16_t def_max_hbrake_time = 0; +static const uint16_t def_btn_force_repeat_time = 300; +static const uint16_t def_btn_mode_repeat_time = 700; +static const uint8_t def_button_inputs = 1; + +static const uint8_t def_display_brigth = 100; +static const uint8_t def_display_dimm = 50; + +static const uint16_t cv_ref_resistance = 1500; +static const uint16_t cc_ref_resistance = 2000; +static const uint16_t cc_min_resistance = 1000; + +static const uint8_t bmode_image_open = 0x07; +static const uint8_t bmode_image_user = 0x1E; +static const uint8_t bmode_image_lock = 0x38; + +static const uint16_t display_keep_bmode = 2000; +static const uint16_t display_keep_userf = 1000; + +static const uint8_t user_force_step = 10; + +static const uint8_t def_btn_force = 0; +static const uint8_t def_brake_mode = 0; + +static const uint16_t def_chasis_inp_debounce = 100; +static const uint16_t def_user_inp_debounce = 20; + +static const uint16_t mem_addr_inp_mode = 0; +static const uint16_t mem_addr_force = 1; +static const uint16_t mem_addr_bmode = 2; +static const uint16_t mem_addr_dsp_brigth = 3; +static const uint16_t mem_addr_dsp_dimm = 4; +static const uint16_t mem_addr_lock_current = 5; +static const uint16_t mem_addr_hbrake_time = 7; + /**** Private variables ****/ /**** Private function declarations ****/ static uint8_t img_gen_dot10(uint8_t percent); @@ -23,53 +59,54 @@ dccd::DccdApp::~DccdApp(void) return; } -void dccd::DccdApp::init(void) +void dccd::DccdApp::init(DccdHw* dccd_hw) { - // Read settings from EEPROM + this->hardware = dccd_hw; - DccdHw::dccdHwCfg_t cfg; - cfg.handbrake_pull_up = 1; - cfg.pwm_f_khz = 16; - cfg.speed_hall = 0; + #define OVERRIDEDEDBNC + #ifdef OVERRIDEDEDBNC + this->hardware->btn_mode.dbnc_lim = def_user_inp_debounce; + this->hardware->btn_up.dbnc_lim = def_user_inp_debounce; + this->hardware->btn_down.dbnc_lim = def_user_inp_debounce; + this->hardware->handbrake.dbnc_lim = def_chasis_inp_debounce; + this->hardware->brakes.dbnc_lim = def_chasis_inp_debounce; + this->hardware->dimm.dbnc_lim = def_chasis_inp_debounce; + #endif - this->hardware.init(&cfg); + // Load saved config from memory + this->loadMemCfg(); - this->lock_current = 4500; - this->max_hbrake_time = 1500; - this->btn_repeat_time = 500; - this->button_inputs = 1; - - this->display_brigth = 100; - this->display_dimm = 50; - - this->btn_force = 0; + this->btn_force_repeat_time = def_btn_force_repeat_time; + this->btn_mode_repeat_time = def_btn_mode_repeat_time; this->pot_force = 0; - this->brake_mode = 0; - this->hardware.read(); + this->hardware->read(); + this->hardware->dimm.force_read(); + + this->hardware->outreg.write_voltage(0); + this->hardware->outreg.write_current(0); + this->hardware->outreg.write_on(1); - this->hardware.outreg.voltage = 0; - this->hardware.outreg.current = 0; - this->hardware.outreg.out_on = 1; - this->hardware.display.write(0x01); - this->hardware.display.write_backlight(50); + this->hardware->display.write(0x01); + if(this->hardware->dimm.state) this->hardware->display.write_backlight(this->display_dimm); + else this->hardware->display.write_backlight(this->display_brigth); - this->hardware.write(); + this->hardware->write(); } void dccd::DccdApp::process(void) { // Update all inputs - this->hardware.read(); + this->hardware->read(); uint8_t is_new_mode = 0; uint8_t is_new_btn_force = 0; // Process mode button - if((this->hardware.btn_mode.state==1)&&((this->hardware.btn_mode.is_new)||(this->hardware.btn_mode.time_read() >= this->btn_repeat_time))) + if((this->hardware->btn_mode.state==1)&&((this->hardware->btn_mode.is_new)||(this->hardware->btn_mode.time_read() >= this->btn_mode_repeat_time))) { - this->hardware.btn_mode.time_reset(); - this->hardware.btn_mode.is_new = 0; + this->hardware->btn_mode.time_reset(); + this->hardware->btn_mode.is_new = 0; // Change mode switch(this->brake_mode) { @@ -86,30 +123,36 @@ void dccd::DccdApp::process(void) break; } is_new_mode = 1; + this->hardware->board_hw.nvmem.write_8b(mem_addr_bmode, this->brake_mode); }; // Process user force inputs - if((this->hardware.btn_up.state==1)&&((this->hardware.btn_up.is_new)||(this->hardware.btn_up.time_read() >= this->btn_repeat_time))) + if((this->hardware->btn_up.state==1)&&((this->hardware->btn_up.is_new)||(this->hardware->btn_up.time_read() >= this->btn_force_repeat_time))) { - this->hardware.btn_up.time_reset(); - this->hardware.btn_up.is_new = 0; + this->hardware->btn_up.time_reset(); + this->hardware->btn_up.is_new = 0; // Increase user force - this->btn_force += 10; + this->btn_force += user_force_step; if(this->btn_force > 100) this->btn_force = 100; is_new_btn_force = 1; }; - if((this->hardware.btn_down.state==1)&&((this->hardware.btn_down.is_new)||(this->hardware.btn_down.time_read() >= this->btn_repeat_time))) + if((this->hardware->btn_down.state==1)&&((this->hardware->btn_down.is_new)||(this->hardware->btn_down.time_read() >= this->btn_force_repeat_time))) { - this->hardware.btn_down.time_reset(); - this->hardware.btn_down.is_new = 0; + this->hardware->btn_down.time_reset(); + this->hardware->btn_down.is_new = 0; // Decrease user force - this->btn_force -= 10; + this->btn_force -= user_force_step; if(this->btn_force > 100) this->btn_force = 0; is_new_btn_force = 1; }; - this->pot_force = this->hardware.pot.last_percent; + if(is_new_btn_force) + { + this->hardware->board_hw.nvmem.write_8b(mem_addr_force, this->btn_force); + }; + + this->pot_force = this->hardware->pot.last_percent; // Determine user force int8_t user_force; @@ -119,12 +162,18 @@ void dccd::DccdApp::process(void) // Determine next settable force int8_t next_force; - if((this->hardware.handbrake.state == 1)&&(this->hardware.handbrake.time_read() < this->max_hbrake_time)) + uint8_t hbrake_timeout = 0; + if((this->max_hbrake_time!=0)&&(this->hardware->handbrake.time_read() >= this->max_hbrake_time)) + { + hbrake_timeout = 1; + }; + + if((this->hardware->handbrake.state == 1)&&(hbrake_timeout==0)) { // Handbrake override next_force = -1; } - else if(this->hardware.brakes.state == 1) + else if(this->hardware->brakes.state == 1) { // Brakes override switch(this->brake_mode) @@ -142,7 +191,8 @@ void dccd::DccdApp::process(void) break; default: - this->brake_mode = -1; + next_force = -1; + this->brake_mode = 0; break; } } @@ -156,102 +206,227 @@ void dccd::DccdApp::process(void) if(next_force < 0) { // HiZ - this->hardware.outreg.voltage = 0; - this->hardware.outreg.current = 0; - this->hardware.outreg.out_on = 0; + this->hardware->outreg.write_voltage(0); + this->hardware->outreg.write_current(0); + this->hardware->outreg.write_on(0); + // For display + next_force = 0; } else if(next_force == 0) { // Open - this->hardware.outreg.voltage = 0; - this->hardware.outreg.current = 0; - this->hardware.outreg.out_on = 1; + this->hardware->outreg.write_voltage(0); + this->hardware->outreg.write_current(0); + this->hardware->outreg.write_on(1); } else { // Calculate current and voltage settings - this->hardware.outreg.current = util::percent_of((uint8_t)next_force, this->lock_current); - this->hardware.outreg.voltage = util::sat_mul_kilo(this->hardware.outreg.current, 2500); - this->hardware.outreg.out_on = 1; + this->hardware->outreg.write_current(util::percent_of((uint8_t)next_force, this->lock_current)); + uint16_t ref_resistance = cv_ref_resistance; + if(this->hardware->outreg.cc_mode_en) ref_resistance = cc_ref_resistance; + this->hardware->outreg.write_voltage(util::sat_mul_kilo(this->hardware->outreg.read_current(), ref_resistance)); + this->hardware->outreg.write_on(1); } // Display image if(is_new_mode) { - uint8_t image; + uint8_t bmode_image; switch(this->brake_mode) { case 0: - image = 0x07; + bmode_image = bmode_image_open; break; case 1: - image = 0x1E; + bmode_image = bmode_image_user; break; case 2: - image = 0x38; + bmode_image = bmode_image_lock; break; default: - image = 0x07; + bmode_image = bmode_image_open; + this->brake_mode = 0; break; } - this->hardware.display.write(image, 1000, 1000, 1); + this->hardware->display.write(bmode_image, display_keep_bmode, display_keep_bmode, 1); + is_new_mode = 0; } - else if(is_new_btn_force) + else if((is_new_btn_force)&&(this->button_inputs)) { - this->hardware.display.write(img_gen_dot10(this->btn_force), 500, 500, 1); + this->hardware->display.write(img_gen_dot10(this->btn_force), display_keep_userf, display_keep_userf, 1); + is_new_btn_force = 0; } - else if(this->hardware.display.is_cycle_end()) + else if(this->hardware->display.is_cycle_end()) { - this->hardware.display.write(img_gen_dot10(next_force)); + this->hardware->display.write(img_gen_dot10((uint8_t)next_force)); }; // Display backlight - if(this->hardware.dimm.is_new) + if(this->hardware->dimm.is_new) { - this->hardware.dimm.is_new = 0; - if(this->hardware.dimm.state) this->hardware.display.write_backlight(this->display_dimm); - else this->hardware.display.write_backlight(this->display_brigth); + this->hardware->dimm.is_new = 0; + if(this->hardware->dimm.state) this->hardware->display.write_backlight(this->display_dimm); + else this->hardware->display.write_backlight(this->display_brigth); }; // Execute outputs - this->hardware.write(); + this->hardware->write(); +} + +uint8_t dccd::DccdApp::loadMemCfg(void) +{ + // Load saved config from memory + uint8_t t1; + uint16_t t2; + uint8_t def_applied = 0; + + t1 = this->hardware->board_hw.nvmem.read_8b(mem_addr_inp_mode); + if(t1 > 1){this->button_inputs = def_button_inputs; def_applied=1; } + else this->button_inputs = t1; + + t1 = this->hardware->board_hw.nvmem.read_8b(mem_addr_force); + if(t1 > 100){this->btn_force = def_btn_force; def_applied=1; } + else this->btn_force = t1; + + t1 = this->hardware->board_hw.nvmem.read_8b(mem_addr_bmode); + if(t1 > 2){this->brake_mode = def_brake_mode; def_applied=1; } + else this->brake_mode = t1; + + t1 = this->hardware->board_hw.nvmem.read_8b(mem_addr_dsp_brigth); + if((t1 > 100)||(t1 == 0)){this->display_brigth = def_brake_mode; def_applied=1; } + else this->display_brigth = t1; + + t1 = this->hardware->board_hw.nvmem.read_8b(mem_addr_dsp_dimm); + if((t1 > 100)||(t1 == 0)){this->display_dimm = def_brake_mode; def_applied=1; } + else this->display_dimm = t1; + + t2 = this->hardware->board_hw.nvmem.read_16b(mem_addr_lock_current); + if((t2 > 5000)||(t2 < 1000)){this->lock_current = def_lock_current; def_applied=1; } + else this->lock_current = t2; + + t2 = this->hardware->board_hw.nvmem.read_16b(mem_addr_hbrake_time); + if((t2 > 30000)||(t2 == 0)){this->max_hbrake_time = def_max_hbrake_time; def_applied=1; } + else this->max_hbrake_time = t2; + + return def_applied; +} + +void dccd::DccdApp::saveMemCfg(void) +{ + // Save config to memory + + this->hardware->board_hw.nvmem.write_8b(mem_addr_inp_mode, this->button_inputs); + + this->hardware->board_hw.nvmem.write_8b(mem_addr_force, this->btn_force); + + this->hardware->board_hw.nvmem.write_8b(mem_addr_bmode, this->brake_mode); + + this->hardware->board_hw.nvmem.write_8b(mem_addr_dsp_brigth, this->display_brigth); + + this->hardware->board_hw.nvmem.write_8b(mem_addr_dsp_dimm, this->display_dimm); + + this->hardware->board_hw.nvmem.write_16b(mem_addr_lock_current, this->lock_current); + + this->hardware->board_hw.nvmem.write_16b(mem_addr_hbrake_time, this->max_hbrake_time); } /**** Private function definitions ***/ static uint8_t img_gen_dot10(uint8_t percent) { - if(percent<6) return 0x01; - else if(percent<16) return 0x03; - else if(percent<26) return 0x02; - else if(percent<36) return 0x06; - else if(percent<46) return 0x04; - else if(percent<56) return 0x0C; - else if(percent<66) return 0x08; - else if(percent<76) return 0x18; - else if(percent<86) return 0x10; - else if(percent<96) return 0x30; - else return 0x20; + switch(percent) + { + case 0 ... 5: + return 0x01; + + case 6 ... 15: + return 0x03; + + case 16 ... 25: + return 0x02; + + case 26 ... 35: + return 0x06; + + case 36 ... 45: + return 0x04; + + case 46 ... 55: + return 0x0C; + + case 56 ... 65: + return 0x08; + + case 66 ... 75: + return 0x18; + + case 76 ... 85: + return 0x10; + + case 86 ... 95: + return 0x30; + + case 96 ... 100: + return 0x20; + + default: + return 0x20; + } } static uint8_t img_gen_dot20(uint8_t percent) { - if(percent<11) return 0x01; - else if(percent<31) return 0x02; - else if(percent<51) return 0x04; - else if(percent<71) return 0x08; - else if(percent<91) return 0x10; - else return 0x20; + switch(percent) + { + case 0 ... 10: + return 0x01; + + case 11 ... 30: + return 0x02; + + case 31 ... 50: + return 0x04; + + case 51 ... 70: + return 0x08; + + case 71 ... 90: + return 0x10; + + case 91 ... 100: + return 0x20; + + default: + return 0x20; + } } static uint8_t img_gen_bar(uint8_t percent) { - if(percent<11) return 0x01; - else if(percent<31) return 0x03; - else if(percent<51) return 0x07; - else if(percent<71) return 0x0F; - else if(percent<91) return 0x1F; - else return 0x3F; + switch(percent) + { + case 0 ... 10: + return 0x01; + + case 11 ... 30: + return 0x03; + + case 31 ... 50: + return 0x07; + + case 51 ... 70: + return 0x0F; + + case 71 ... 90: + return 0x1F; + + case 91 ... 100: + return 0x3F; + + default: + return 0x3F; + } } diff --git a/firmware/src/dccd/dccd.h b/firmware/src/dccd/dccd.h index a723aa2..bacd4db 100644 --- a/firmware/src/dccd/dccd.h +++ b/firmware/src/dccd/dccd.h @@ -14,12 +14,13 @@ class DccdApp DccdApp(void); ~DccdApp(void); - void init(void); + void init(DccdHw* dccd_hw); void process(void); uint16_t lock_current; uint16_t max_hbrake_time; - uint16_t btn_repeat_time; + uint16_t btn_force_repeat_time; + uint16_t btn_mode_repeat_time; uint8_t button_inputs; uint8_t display_brigth; uint8_t display_dimm; @@ -28,10 +29,13 @@ class DccdApp uint8_t pot_force; uint8_t brake_mode; + uint8_t loadMemCfg(void); + void saveMemCfg(void); + #ifdef TESTING protected: #endif - dccd::DccdHw hardware; + DccdHw* hardware; }; /**** Public function declarations ****/ diff --git a/firmware/src/dccd/dccd_hw.cpp b/firmware/src/dccd/dccd_hw.cpp index 7bceec4..4270771 100644 --- a/firmware/src/dccd/dccd_hw.cpp +++ b/firmware/src/dccd/dccd_hw.cpp @@ -6,6 +6,37 @@ using namespace dccd; /**** Private definitions ****/ /**** Private constants ****/ +static const uint8_t def_dbnc_time = 10; + +static const uint16_t def_pot_dead_bot = 500; +static const uint16_t def_pot_dead_top = 4500; + +static const uint8_t def_cc_mode_en = 1; + +static const uint16_t def_cnter_us = 900; + +static const uint16_t def_out_voltage_under_treshold = 0; +static const uint16_t def_out_voltage_over_treshold = 9000; +static const uint16_t def_out_voltage_hold_time = 1000; +static const uint16_t def_out_voltage_cooldown_time = 0; + +static const uint16_t def_out_current_under_treshold = 0; +static const uint16_t def_out_current_over_treshold = 6000; +static const uint16_t def_out_current_hold_time = 200; +static const uint16_t def_out_current_cooldown_time = 1000; + +static const uint16_t def_battery_voltage_under_treshold = 9000; +static const uint16_t def_battery_voltage_over_treshold = 18000; +static const uint16_t def_battery_voltage_hold_time = 1000; +static const uint16_t def_battery_voltage_cooldown_time = 0; + +static const uint16_t def_battery_current_under_treshold = 0; +static const uint16_t def_battery_current_over_treshold = 8000; +static const uint16_t def_battery_current_hold_time = 200; +static const uint16_t def_battery_current_cooldown_time = 1000; + +static const uint16_t def_inital_bat_voltage = 12000; + /**** Private variables ****/ /**** Private function declarations ****/ /**** Public function definitions ****/ @@ -20,66 +51,68 @@ dccd::DccdHw::~DccdHw(void) } void dccd::DccdHw::init(dccdHwCfg_t* cfg) -{ +{ + // Apply config bsp::Board::boardCfg_t board_cfg; board_cfg.pwm_f_khz = cfg->pwm_f_khz; board_cfg.od_common_is_pwm = 1; this->board_hw.init(&board_cfg); - this->counter.init(0xFFFF, 900); + this->counter.init(0xFFFF, cfg->counter_step_us); + this->counter.disabled = 0; this->out_voltage.init(&(this->board_hw.out_voltage), &(this->counter)); - this->out_voltage.under_treshold = 0; - this->out_voltage.over_treshold = 9000; - this->out_voltage.hold_time = 1000; - this->out_voltage.cooldown_time = 0; + this->out_voltage.under_treshold = def_out_voltage_under_treshold; + this->out_voltage.over_treshold = def_out_voltage_over_treshold; + this->out_voltage.hold_time = def_out_voltage_hold_time; + this->out_voltage.cooldown_time = def_out_voltage_cooldown_time; this->out_voltage.update_ain = 0; this->out_voltage.auto_reset = 1; this->out_current.init(&(this->board_hw.out_current), &(this->counter)); - this->out_current.under_treshold = 0; - this->out_current.over_treshold = 6000; - this->out_current.hold_time = 200; - this->out_current.cooldown_time = 1000; + this->out_current.under_treshold = def_out_current_under_treshold; + this->out_current.over_treshold = def_out_current_over_treshold; + this->out_current.hold_time = def_out_current_hold_time; + this->out_current.cooldown_time = def_out_current_cooldown_time; this->out_current.update_ain = 0; this->out_current.auto_reset = 1; this->battery_voltage.init(&(this->board_hw.battery_voltage), &(this->counter)); - this->battery_voltage.under_treshold = 9000; - this->battery_voltage.over_treshold = 18000; - this->battery_voltage.hold_time = 1000; - this->battery_voltage.cooldown_time = 0; + this->battery_voltage.under_treshold = def_battery_voltage_under_treshold; + this->battery_voltage.over_treshold = def_battery_voltage_over_treshold; + this->battery_voltage.hold_time = def_battery_voltage_hold_time; + this->battery_voltage.cooldown_time = def_battery_voltage_cooldown_time; this->battery_voltage.update_ain = 0; this->battery_voltage.auto_reset = 1; - this->battery_voltage.last_read = 12000; + this->battery_voltage.last_read = def_inital_bat_voltage; this->battery_current.init(&(this->board_hw.battery_current), &(this->counter)); - this->battery_current.under_treshold = 0; - this->battery_current.over_treshold = 8000; - this->battery_current.hold_time = 200; - this->battery_current.cooldown_time = 1000; + this->battery_current.under_treshold = def_battery_current_under_treshold; + this->battery_current.over_treshold = def_battery_current_over_treshold; + this->battery_current.hold_time = def_battery_current_hold_time; + this->battery_current.cooldown_time = def_battery_current_cooldown_time; this->battery_current.update_ain = 0; this->battery_current.auto_reset = 1; - this->btn_up.init(&(this->board_hw.din4), 0, &(this->counter), 10); + this->btn_up.init(&(this->board_hw.din4), 0, &(this->counter), def_dbnc_time); this->btn_up.update_din = 0; - this->btn_down.init(&(this->board_hw.din3), 0, &(this->counter), 10); + this->btn_down.init(&(this->board_hw.din3), 0, &(this->counter), def_dbnc_time); this->btn_down.update_din = 0; - this->btn_mode.init(&(this->board_hw.din1), 0, &(this->counter), 10); + this->btn_mode.init(&(this->board_hw.din1), 0, &(this->counter), def_dbnc_time); this->btn_mode.update_din = 0; - this->handbrake.init(&(this->board_hw.hvdin3), 0, &(this->counter), 10); + this->handbrake.init(&(this->board_hw.hvdin3), 0, &(this->counter), def_dbnc_time); this->handbrake.update_din = 0; - this->brakes.init(&(this->board_hw.hvdin2), 1, &(this->counter), 10); + this->brakes.init(&(this->board_hw.hvdin2), 1, &(this->counter), def_dbnc_time); this->brakes.update_din = 0; - this->dimm.init(&(this->board_hw.hvdin1), 1, &(this->counter), 10); + this->dimm.init(&(this->board_hw.hvdin1), 1, &(this->counter), def_dbnc_time); this->dimm.update_din = 0; - this->pot.init(&(this->board_hw.ain2), 500, 4500); + this->pot.init(&(this->board_hw.ain2), def_pot_dead_bot, def_pot_dead_top); this->pot.update_ain = 0; hw::OutReg::outRegCfg_t outreg_cfg; @@ -89,6 +122,8 @@ void dccd::DccdHw::init(dccdHwCfg_t* cfg) outreg_cfg.uout = &this->board_hw.out_voltage; outreg_cfg.iout = &this->board_hw.out_current; this->outreg.init(&outreg_cfg); + this->outreg.cc_mode_en = def_cc_mode_en; + this->outreg.update_ain = 0; hw::LedDisplay::doutCfg_t dsp_cfg; dsp_cfg.led0_dout_ch = &(this->board_hw.od1); @@ -114,12 +149,10 @@ void dccd::DccdHw::init(dccdHwCfg_t* cfg) else this->board_hw.freq_pull.write(0); // Set initial output states - this->outreg.voltage = 0; - this->outreg.current = 0; - this->outreg.out_on = 0; - this->outreg.lock = 0; - this->outreg.cc_mode_en = 0; - this->outreg.update_ain = 0; + this->outreg.write_voltage(0); + this->outreg.write_current(0); + this->outreg.write_on(0); + this->outreg.write_lock(0); this->outreg.process(); this->display.write_backlight(100); diff --git a/firmware/src/dccd/dccd_hw.h b/firmware/src/dccd/dccd_hw.h index 6ed8897..3ccdd0c 100644 --- a/firmware/src/dccd/dccd_hw.h +++ b/firmware/src/dccd/dccd_hw.h @@ -22,6 +22,7 @@ class DccdHw uint8_t pwm_f_khz; uint8_t handbrake_pull_up; uint8_t speed_hall; + uint16_t counter_step_us; } dccdHwCfg_t; DccdHw(void); diff --git a/firmware/src/hw/led_display.cpp b/firmware/src/hw/led_display.cpp index 867e7f0..ac525f4 100644 --- a/firmware/src/hw/led_display.cpp +++ b/firmware/src/hw/led_display.cpp @@ -41,6 +41,7 @@ void hw::LedDisplay::init(doutCfg_t* dout_chs, uint8_t act_lvl, util::VCounter* this->cycle_cnt = 0; this->cycle_limit = 0; this->timestamp_start = 0; + this->image = 0x00; this->force(0x00); this->write_backlight(0); @@ -83,9 +84,10 @@ void hw::LedDisplay::write(uint8_t image) this->cycle_cnt = 0; this->cycle_limit = 0; this->timestamp_start = 0; + this->image = image; // Set initial state - this->force(image); + this->force(this->image); } void hw::LedDisplay::write(uint8_t image, uint16_t on_time, uint16_t period, uint8_t cycle_limit) @@ -95,9 +97,10 @@ void hw::LedDisplay::write(uint8_t image, uint16_t on_time, uint16_t period, uin this->period = period; this->cycle_cnt = 0; this->cycle_limit = cycle_limit; + this->image = image; // Set initial state - if(this->on_time > 0) this->force(image); + if(this->on_time > 0) this->force(this->image); else this->force(0x00); // Cycle start time diff --git a/firmware/src/hw/out_reg.cpp b/firmware/src/hw/out_reg.cpp index 2e9f8ff..da62478 100644 --- a/firmware/src/hw/out_reg.cpp +++ b/firmware/src/hw/out_reg.cpp @@ -35,6 +35,39 @@ void hw::OutReg::init(outRegCfg_t* cfg) this->cc_mode_en = 0; this->update_ain = 0; + this->cc_tolerance = 75; +} + +void hw::OutReg::write_voltage(uint16_t voltage) +{ + this->voltage = voltage; +} + +void hw::OutReg::write_current(uint16_t current) +{ + this->current = current; + this->current_bot = util::sat_subtract(current, this->cc_tolerance); + this->current_top = util::sat_add(current, this->cc_tolerance); +} + +void hw::OutReg::write_on(uint8_t state) +{ + this->out_on = state; +} + +void hw::OutReg::write_lock(uint8_t state) +{ + this->lock = state; +} + +uint16_t hw::OutReg::read_voltage(void) +{ + return this->voltage; +} + +uint16_t hw::OutReg::read_current(void) +{ + return this->current; } void hw::OutReg::process(void) @@ -61,20 +94,23 @@ void hw::OutReg::process(void) }; // Calculate next duty cycle setting - uint16_t next_duty; + uint16_t next_duty = this->pwm_high->get_set_duty(); if((this->voltage==0)||(this->current==0)) { // Off but not HiZ next_duty = 0; } - else if((this->cc_mode_en)&&(this->iout->last_read > this->current)) + else if((this->cc_mode_en)&&(this->iout->last_read > this->current_bot)) { - // Constant current mode - // Reduce voltage to be within current limit - uint32_t temp = (uint32_t)this->pwm_high->get_set_duty() * (uint32_t)this->current; - temp /= this->iout->last_read; - next_duty = util::sat_cast(temp); + // Constant current mode - Change voltage to be within current limit + if(util::is_in_range(this->iout->last_read, this->current_bot, this->current_top)==0) + { + // Current outside of tolerance. Recalculate duty cycle. + uint32_t temp = (uint32_t)this->pwm_high->get_set_duty() * (uint32_t)this->current; + temp /= this->iout->last_read; + next_duty = util::sat_cast(temp); + }; } else { diff --git a/firmware/src/hw/out_reg.h b/firmware/src/hw/out_reg.h index 2b93ab1..54ca57a 100644 --- a/firmware/src/hw/out_reg.h +++ b/firmware/src/hw/out_reg.h @@ -25,13 +25,17 @@ class OutReg void init(outRegCfg_t* cfg); - uint16_t voltage; - uint16_t current; - uint8_t out_on; - uint8_t lock; - uint8_t cc_mode_en; uint8_t update_ain; + uint16_t cc_tolerance; + + void write_voltage(uint16_t voltage); + void write_current(uint16_t current); + void write_on(uint8_t state); + void write_lock(uint8_t state); + + uint16_t read_voltage(void); + uint16_t read_current(void); void process(void); void force_off(void); @@ -44,6 +48,12 @@ class OutReg bsp::AnalogIn* ubat; bsp::AnalogIn* uout; bsp::AnalogIn* iout; + uint16_t voltage; + uint16_t current; + uint16_t current_top; + uint16_t current_bot; + uint8_t out_on; + uint8_t lock; }; /**** Public function declarations ****/ diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 2da7942..f1f6d4c 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,38 +1,56 @@ /**** Includes ****/ #include "utils/utils.h" +#include "dccd/dccd_hw.h" #include "dccd/dccd.h" /**** Private definitions ****/ /**** Private constants ****/ /**** Private variables ****/ +static dccd::DccdHw dccd_hw; +static dccd::DccdApp app; /**** Private function declarations ****/ /**** Public function definitions ****/ int main(void) { // Setup - dccd::DccdApp dccd_app; + dccd::DccdHw::dccdHwCfg_t cfg; + cfg.handbrake_pull_up = 1; + cfg.pwm_f_khz = 16; + cfg.speed_hall = 0; + cfg.counter_step_us = 2000; + dccd_hw.init(&cfg); - dccd_app.init(); + app.init(&dccd_hw); - dccd_app.lock_current = 4500; - dccd_app.max_hbrake_time = 1000; - dccd_app.btn_repeat_time = 500; - dccd_app.button_inputs = 1; - dccd_app.display_brigth = 100; - dccd_app.display_dimm = 50; + //#define OVERRIDECFG + #ifdef OVERRIDECFG + // Configuration + app.lock_current = 4500; + app.max_hbrake_time = 2000; + app.button_inputs = 1; + app.display_brigth = 100; + app.display_dimm = 25; - dccd_app.btn_force = 0; - dccd_app.pot_force = 0; - dccd_app.brake_mode = 0; + // Initial values + app.btn_force = 0; + app.brake_mode = 0; + #endif + + // Save config to memory + //#define SAVECFG + #ifdef SAVECFG + app.saveMemCfg(); + #endif // Super loop while(1) { - dccd_app.process(); - - continue; // End of super loop + // Do stuff + app.process(); + // End of super loop + continue; } // Escape the matrix diff --git a/firmware/src/uDCCD.cppproj b/firmware/src/uDCCD.cppproj index 7919a1a..983558e 100644 --- a/firmware/src/uDCCD.cppproj +++ b/firmware/src/uDCCD.cppproj @@ -40,6 +40,22 @@ + com.atmel.avrdbg.tool.atmelice + J42700001490 + 0x1E9516 + + + + 249992 + + ISP + + com.atmel.avrdbg.tool.atmelice + J42700001490 + Atmel-ICE + + ISP + 249992 -- 2.49.1