#include "main.h" #include "system_application.h" #include "stdbool.h" #include "string.h" #include "utility.h" uint16_t icharger_battery_current_copy = 0; // Variabile per salvare il valore di corrente della batteria, usata per decidere se batteria piena uint16_t ups_undervoltage_recursive_error = 0; #define MAX_UPS_UNDERVOLTAGE_RECURSIVE_ERROR 3 float Night_due_tab_pot[][7] = { { 0.1, 0.1, 0.1, 6.4, 0.1, 0.1, 0.1 }, { 0.1, 0.1, 0.3, 6, 0.3, 0.1, 0.1 }, { 0.1, 0.3, 0.7, 4.8, 0.7, 0.3, 0.1 }, { 0.1, 0.7, 0.7, 4, 0.7, 0.7, 0.1 }, { 0.1, 0.9, 0.9, 3.2, 0.9, 0.9, 0.1 }, { 0.7, 0.7, 0.7, 2.8, 0.7, 0.7, 0.7 }, { 0.834, 0.834, 0.834, 2, 0.834, 0.834, 0.834 }, { 1, 1, 1, 1, 1, 1, 1 }, { 1.083, 1.083, 1.083, 0.5, 1.083, 1.083, 1.083 }, { 1.16, 1.16, 1.16, 0, 1.16, 1.16, 1.16 }, { 1.35, 1.35, 0.8, 0, 0.8, 1.35, 1.35 }, { 1.625, 1.625, 0.25, 0, 0.25, 1.625, 1.625 }, { 1.75, 1.75, 0, 0, 0, 1.75, 1.75 }, { 2.5, 1, 0, 0, 0, 1, 2.5 }, { 3, 0.5, 0, 0, 0, 0.5, 3 }, { 3.5, 0, 0, 0, 0, 0, 3.5 } }; uint16_t X_int_temp_table[] = { 4012, 3986, 3952, 3910, 3859, 3796, 3721, 3633, 3530, 3413, 3280, 3135, 2977, 2808, 2632, 2451, 2268, 2087, 1910, 1739, 1577, 1424, 1283, 1152, 1033, 925, 827, 740, 661 }; int16_t Y_int_temp_table[] = { -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120 }; uint16_t X_bat_temp_table[] = { 4012, 3986, 3952, 3910, 3859, 3796, 3721, 3633, 3530, 3413, 3280, 3135, 2977, 2808, 2632, 2451, 2268, 2087, 1910, 1739, 1577, 1424, 1283, 1152, 1033, 925, 827, 740, 661 }; int16_t Y_bat_temp_table[] = { -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120 }; float V_Tcomp_VoeC_Pb[] = { 0.98, 0.86, 0.72, 0.58, 0.42, 0.28, 0.12, 0, -0.14, -0.26, -0.36, -0.44, -0.52, -0.6, -0.64 }; //-10, -5, 0, 5, 10, 15, 20, 25,30, 35, 40, 45, 50, 55, 60 System_tempe_table_t System_tempe_table_internal = { Y_int_temp_table, X_int_temp_table, sizeof(X_int_temp_table) / sizeof(X_int_temp_table[0]) }; System_tempe_table_t System_tempe_table_battery = { Y_bat_temp_table, X_bat_temp_table, sizeof(X_bat_temp_table) / sizeof(X_bat_temp_table[0]) }; struct { uint8_t timed_state[64]; uint8_t total_step; uint8_t counter; } led_tabs_array; // Gestisce la PWM del pannello o UPS // Chiamata a fine conversione ADC (eseguita ogni 250uS) così da essere sincrona con le letture void System_application_timed(void) { // ogni 250us chiamata da interrupt ma poi chiamata da main //---------------------------------------------------------- if (SystemTimer.delayed_UPS_start >= 0) return; // Se è in conto alla rovescia per l'avvio ritardato dell'UPS non esegue la gestione del PWM if ((enVDRV_GPIO_Port->IDR & enVDRV_Pin) == 0 || htim1.State == HAL_TIM_STATE_READY) { // Se è attivo il driver del pannello, altrimenti è attivo quello dell'UPS Stop_Photovoltaic_PWM(); Valuate_start_Photovoltaic_PWM_Value(SystemFlags.ups_rele ? true : false); HAL_GPIO_WritePin(enVDRV_GPIO_Port, enVDRV_Pin, GPIO_PIN_SET); Start_Photovoltaic_PWM(); return; } //----------------------------------------------------------- if (aADCxConvertedData[AN_VBAT_enum] >= System_var.vOvervoltage_bit) System_set_error_flag(ER_OVERVOLT); // Sostituisce --> SystemFlags.overvoltage = 1; if (aADCxConvertedData[AN_TBAT_enum] < X_bat_temp_table[20 - (SystemFlags.overBattemperature ? 1 : 0)]) { // Con isteresi in caso di errore System_set_error_flag(ER_OVER_BAT_TEMP); } if ((((SystemFlags.ups_rele) ? aADCxConvertedData[AN_VPANP_enum] : aADCxConvertedData[AN_VUPS_enum]) - aADCxConvertedData[AN_VPANM_enum]) > System_var.vPanMax_bit) { if (SystemFlags.ups_rele) System_set_error_flag(ER_OVERVOLTUPS); else System_set_error_flag(ER_OVERVOLTPV); } if (SystemFlags.sub_16_flag_0 & 0x1B4 /* considera solo i flag overvolt, overvoltpv, overvoltups e lowBattemperature, overBattemperature */) { Stop_Photovoltaic_PWM(); // TO DO gli overvoltage SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; } else { int8_t local_add = -1; bool check_limit_current_for_mppt = false; uint16_t iChargeMax_bit_local = 0; if (SystemFlags.ups_rele){ uint16_t sustain = (System_setup_var.Setup_LedMode == ALWAYS_ON) ? 210 : 10; // prima era 21; /* 21 = circa 100mA*/ iChargeMax_bit_local = (aADCxConvertedData[AN_ILED_enum] * coef_conversion.var_ILAMP_CALC_mAMPERE_COEF_BIT) + System_var.iCharge_bias_bit + sustain + (System_setup_var.Setup_UPS_mA_to_Batt / (coef_conversion.var_ICHARGE_AMPERE_BIT * 1000)); } else { iChargeMax_bit_local = System_var.iChargeMax_bit - System_var.derating_temp; } if (aADCxConvertedData[AN_ICHG_enum] >= iChargeMax_bit_local) { check_limit_current_for_mppt = true; // SystemTimer.MPPT_pause_timer_ms = MPPT_TASK_PAUSE_MS; if (SystemFlags.search_min_voltage_point_at_max_current && (System_var.system_pwm_ditter_duty == System_var.max_setpoint_duty_ditter || aADCxConvertedData[AN_VBAT_enum] > System_var.vEoCharge_bit)) { SystemFlags.search_min_voltage_point_at_max_current = false; Stop_Photovoltaic_PWM(); SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; LED_R_GPIO_Port->BRR = LED_R_Pin; } if (SystemFlags.search_min_voltage_point_at_max_current /*&& aADCxConvertedData[AN_VBAT_enum] <= System_var.vEoCharge_bit*/&& SystemFlags.ups_rele == 0) { local_add = 1; } } else { if (aADCxConvertedData[AN_VBAT_enum] <= (System_var.vEoCharge_bit + System_var.vEoC_delta_Flood_bit)) { local_add = 1; if (System_mppt.counter_to_stop_MPPT_To_Veoc_reach > 0) // per il check di fine carica System_mppt.counter_to_stop_MPPT_To_Veoc_reach--; if (SystemTimer.battery_full_delay_timer_ms > 0) // Qui gestisco il timer per decidere se batteria carica, sarà sempre incrementato nel systick e qui invece decrementato SystemTimer.battery_full_delay_timer_ms--; // se riesce a raggiungere il valore massimo vuol dire che la batteria è rimasta per tutto il tempo a VoC, e se la corrente scende sotto soglia vorrà dire che è carica. // Posso lasciarlo solo qui? Oppure l'incremento dovrei farlo solo nell'else? Provo prima la seconda. } else { if (aADCxConvertedData[AN_ICHG_enum] < (System_var.iCharge_bias_bit)) local_add = 1; else { if (System_mppt.counter_to_stop_MPPT_To_Veoc_reach < 1200) System_mppt.counter_to_stop_MPPT_To_Veoc_reach++; } if (SystemTimer.battery_full_delay_timer_ms < BATTERY_FULL_DELAY_MS) SystemTimer.battery_full_delay_timer_ms++; } } if (System_mppt.flags.do_mppt_or_just_update /*&& SystemTimer.MPPT_pause_timer_ms <= 0*/&& SystemFlags.ups_rele == 0) { if (System_mppt.counter_to_stop_MPPT_To_Veoc_reach < 300) { if (System_var.system_pwm_ditter_duty > System_mppt.dutyCycle_targhet || check_limit_current_for_mppt) { if (SystemFlags.search_min_voltage_point_at_max_current && check_limit_current_for_mppt) { if (System_var.system_pwm_ditter_duty == System_var.max_setpoint_duty_ditter /*|| aADCxConvertedData[AN_VBAT_enum] > System_var.vEoCharge_bit*/ ) { // SystemFlags.search_min_voltage_point_at_max_current = 0; LED_R_GPIO_Port->BRR = LED_R_Pin; System_var.system_pwm_ditter_duty--; } if (aADCxConvertedData[AN_VBAT_enum] <= (System_var.vEoCharge_bit + System_var.vEoC_delta_Flood_bit)) System_var.system_pwm_ditter_duty++; else System_var.system_pwm_ditter_duty--; System_mppt.dutyCycle_targhet = System_var.system_pwm_ditter_duty; System_mppt.delta_inc_multiplier = 1; } else { System_var.system_pwm_ditter_duty--; if (check_limit_current_for_mppt) { System_mppt.dutyCycle_targhet = System_var.system_pwm_ditter_duty; System_mppt.lastPower = 0; System_mppt.delta_inc_multiplier = 3; } } } else if (System_var.system_pwm_ditter_duty < System_mppt.dutyCycle_targhet) System_var.system_pwm_ditter_duty++; } else { System_application_Stop_Do_Mppt(); if (tasks_to_exec_list.ricalibra_icharge_Task == 0) { tasks_to_exec_list.ricalibra_icharge_Task = 1; // Ricalibro Icharge se non sono in MPPT Stop_Photovoltaic_PWM(); SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; } } } else { if (local_add > 0 && (System_var.system_pwm_ditter_duty < System_var.max_setpoint_duty_ditter)) System_var.system_pwm_ditter_duty++; else if (local_add < 0 && System_var.system_pwm_ditter_duty > System_var.min_setpoint_duty_ditter) System_var.system_pwm_ditter_duty--; else if (System_var.system_pwm_ditter_duty == System_var.max_setpoint_duty_ditter && (aADCxConvertedData[AN_ICHG_enum] - System_var.iCharge_bias_bit) > 20 && SystemFlags.ups_rele == 0) { // Qui parte la gestione da parte dell MPPT System_application_Start_Do_Mppt(); } } icharger_battery_current_copy = aADCxConvertedData[AN_ICHG_enum]; // Copio il valore di corrente della batteria per poterlo usare in altri task } UpdateDitherTable((uint16_t*) &aDitherTable, (uint16_t) (System_var.system_pwm_ditter_duty >> 2), (uint8_t) (System_var.system_pwm_ditter_duty % 4)); } void System_day_state_set(bool day) { if (day) { SystemFlags.day = 1; System_setup_var.Autogestione_due_state = 0; // TODO salvare la durate dell'ultima notte e resettare SystemTimer.last_night_timer_ms = 0 // Magari mettere una soglia per non rovinare eventuali prove... System_setup_var.last_night_ore = SystemTimer.last_night_timer_ms / (1000 * 60 * 60); if (System_setup_var.last_night_ore < 3) System_setup_var.last_night_ore = 3; // Per evitare che la notte duri troppo poco durante le varie prove SystemTimer.last_night_timer_ms = 0; SunnyOne_mode_Load.WhPan_on_day = 0; SunnyOne_mode_Load.WhPan_on_day_bit = 0; } else { SystemFlags.day = 0; LED_R_GPIO_Port->BSRR = LED_R_Pin; // Eseguo i calcoli per i profili dei livelli della lampada in base alla durata dell'ultima notte deve essere fatto solo quando diventa notte System_Midnight_uno_Load(); System_Midnight_due_Load(); System_Autogestione_uno_Load(); System_SunnyOne_mode_Load(); SystemFlags.Battery_filled = 0; memset(SystemTimer.Error_repeat_event_timer_ms.N_error_event_in_a_day, 0, sizeof(SystemTimer.Error_repeat_event_timer_ms.N_error_event_in_a_day)); // Resetto il contatore eventi errori avvenuti System_check_error_flags_to_reset(); // Chiamo il clean degli errori per eliminare eventuali flag tenuti on per aver superato il numero di eventi if (SystemFlags.ups_rele == 0) { SystemFlags.search_min_voltage_point_at_max_current = 1; // setto il flag qui perchè così non si setta ogni volta che riavvio la pwm durante il giorno tasks_to_exec_list.ricalibra_icharge_Task = 0; SystemTimer.battery_full_delay_timer_ms = SAVE_SETUP_TASK_PERIOD_MS; // Permetto il salvataggio se necessario tra un secondo dopo che fa notte } } } void System_check_ups_presence(void) { if ((System_var.vups_p_adc_copy - System_var.vpan_n_adc_copy) > coef_conversion.var_MIN_VPAN_VOLTAGE_START_PWM_BIT) { if (System_counter_event.counter_ups_presence > 0) System_counter_event.counter_ups_presence--; if (System_counter_event.counter_ups_presence <= 0) SystemFlags.ups_found = 1; } else { System_counter_event.counter_ups_presence = COUNTER_EV_UPS_FOUND; SystemFlags.ups_found = 0; if (SystemFlags.ups_rele) { Stop_Photovoltaic_PWM(); System_active_UPS(false); if (SystemTimer.find_under_voltage_ups_error_timer_ms > 0) { ups_undervoltage_recursive_error++; if (ups_undervoltage_recursive_error > MAX_UPS_UNDERVOLTAGE_RECURSIVE_ERROR) { ups_undervoltage_recursive_error = 0; System_set_error_flag(ER_UNDERVOLTUPS); if (System_setup_var.Setup_Max_ImA_Lamp > 380) System_setup_var.Setup_Max_ImA_Lamp = 380; else System_setup_var.Setup_Max_ImA_Lamp /= 2; tasks_to_exec_list.Reflesh_bit_conv_Tasks = 1; // Segnalo che devo aggiornare i bit di conversione } } } } } // Controlla se c'è presenza di fotovoltaico o UPS e gestisce il cambio stato giorno/notte void System_check_photovoltaic_presence(void) { // Eseguito ogni CHECK_VPAN_TASK_PERIOD_MS if ((System_var.vpan_p_adc_copy - System_var.vpan_n_adc_copy) < System_var.vNight_bit /*&& SystemFlags.ups_rele == 0*/) { System_counter_event.counter_night_event = COUNTER_EV_NIGHT_EVENT; if (System_counter_event.counter_day_event-- <= 0) { // contatore evento si ripeta n volte prima di diventare vero if (SystemFlags.day) System_day_state_set(false); } } else if ((System_var.vpan_p_adc_copy - System_var.vpan_n_adc_copy) > System_var.vNight_bit + 25) { // + 0.5 V if ((System_var.vpan_p_adc_copy - System_var.vpan_n_adc_copy) < System_var.vPanMax_bit) { if (System_counter_event.counter_night_event-- <= 0) { System_counter_event.counter_day_event = COUNTER_EV_DAY_EVENT; // Qui la tensione di pannello è superiore al minimo ed inferiore al massimo, presuppongo sia giorno. if (!SystemFlags.Photovoltaic_pwm_active && SystemFlags.ups_rele == 0) { if (aADCxConvertedData[AN_VBAT_enum] < System_var.VRestartCharge_bit) { SystemFlags.battery_can_charge = 1; // Batteria può essere caricata } //Aggiungere controllo se batteria è carica quindi non deve ricominciare la produzione a meno di scendere sotto una certa soglia di tensione, oppure se ci sono errori if (SystemFlags.battery_can_charge && ((System_var.vpan_p_adc_copy - System_var.vpan_n_adc_copy) > MIN_VPAN_VOLTAGE_START_PWM_BIT + 25) && (SystemFlags.sub_16_flag_0 & 0x7F) == 0) { tasks_to_exec_list.Check_VPAN_Tasks = 0; System_var.iCharge_bias_bit = aADCxConvertedData[AN_ICHG_enum] - 5; // offset -5 ricavato sperimentalmente HAL_GPIO_WritePin(enVDRV_GPIO_Port, enVDRV_Pin, GPIO_PIN_SET); //Attivo alimentazione Vdrive (in questa posizione rispetto a Start PWM per garantire la vdrv a 12v all'avvio del pwm (160uS)) Valuate_start_Photovoltaic_PWM_Value(SystemFlags.ups_rele ? true : false); tasks_to_exec_list.system_pwm_Tasks = 1; SystemTimer.start_window_timer_ms = 50; Start_Photovoltaic_PWM(); } } else if (SystemFlags.ups_rele && System_setup_var.flags.ups_force_from_ext == 0 && !SystemFlags.lamp_on) { System_active_UPS(false); ups_undervoltage_recursive_error = 0; } if (!SystemFlags.day) System_day_state_set(true); } } else { if (SystemFlags.ups_rele) System_set_error_flag(ER_OVERVOLTUPS); else System_set_error_flag(ER_OVERVOLTPV); } } } void System_application_Start_Do_Mppt(void) { System_mppt.dutyCycle_targhet = System_var.system_pwm_ditter_duty; System_mppt.counter_to_stop_MPPT_To_Ichg_low_reach = 0; System_mppt.counter_to_stop_MPPT_To_Veoc_reach = 0; System_mppt.flags.do_mppt_or_just_update = 1; } void System_application_Stop_Do_Mppt(void) { System_mppt.flags.do_mppt_or_just_update = 0; } void System_application_MPPT_timed(void) { if (System_mppt.flags.do_mppt_or_just_update /*&& SystemTimer.MPPT_pause_timer_ms <= 0*/&& SystemFlags.ups_rele == 0) { // N.B. Il tutto resta calcolato in BIT così da restare sempre negli interi. // measure the output current int32_t currentHomie = aADCxConvertedData[AN_ICHG_enum] - System_var.iCharge_bias_bit; if (currentHomie < 20) { // check corrente minima impostata a 100 mA System_mppt.counter_to_stop_MPPT_To_Ichg_low_reach++; } else { if (System_mppt.counter_to_stop_MPPT_To_Ichg_low_reach > 0) System_mppt.counter_to_stop_MPPT_To_Ichg_low_reach--; } if (System_mppt.counter_to_stop_MPPT_To_Ichg_low_reach > 10) { System_application_Stop_Do_Mppt(); } // measure the output voltage int32_t voltageDawg = aADCxConvertedData[AN_VBAT_enum]; // calculate the output power System_mppt.currentPower = voltageDawg * currentHomie; if (System_mppt.currentPower < System_mppt.lastPower) { System_mppt.delta_increment *= -1; // flip the sign System_mppt.delta_inc_multiplier = 2; } System_mppt.dutyCycle_targhet += (System_mppt.delta_increment * System_mppt.delta_inc_multiplier); // move the DC by 0.5% if (System_mppt.delta_inc_multiplier < 33) System_mppt.delta_inc_multiplier+=System_mppt.delta_inc_multiplier; if (System_mppt.dutyCycle_targhet > System_var.max_setpoint_duty_ditter) // if the DC is too high System_mppt.dutyCycle_targhet = System_var.max_setpoint_duty_ditter; // set the DC to the max if (System_mppt.dutyCycle_targhet < System_var.min_setpoint_duty_ditter) // if the DC is too high System_mppt.dutyCycle_targhet = System_var.min_setpoint_duty_ditter; } else { System_mppt.dutyCycle_targhet = System_var.system_pwm_ditter_duty; } // update the duty cycle System_mppt.lastPower = System_mppt.currentPower; // keep track of the "last" power SystemTimer.MPPT_to_do_timer_ms = MPPT_TASK_PERIOD_MS; } void System_application_MPPT_doSweep() { // TODO: Implementare, non strettamente necessaria } //---------------- System_Autogestione_uno_Load() ---------------------------------------------- void System_Autogestione_uno_Load() { if (SystemFlags.Battery_filled) { if (System_setup_var.Autogestione_uno_soglia_ora < 12) System_setup_var.Autogestione_uno_soglia_ora++; } else { if (System_setup_var.Autogestione_uno_soglia_ora > 0) System_setup_var.Autogestione_uno_soglia_ora--; } } //---------------- System_Autogestione_uno_Load() ---------------------------------------------- void System_Autogestione_due_Load() { // Setup_VEoc if (System_setup_var.Setup_VEoc < 140) { // *0.1V di fine carica; if (aADCxConvertedData[AN_VBAT_enum] < (11.60 / 0.00415) || System_setup_var.Autogestione_due_state == 3) { System_var.led_lamp_level = 1; System_setup_var.Autogestione_due_state = 3; } else if (aADCxConvertedData[AN_VBAT_enum] < (12.20 / 0.00415) || System_setup_var.Autogestione_due_state == 2) { System_var.led_lamp_level = 2; System_setup_var.Autogestione_due_state = 2; } else if (aADCxConvertedData[AN_VBAT_enum] < (12.50 / 0.00415) || System_setup_var.Autogestione_due_state == 1) { System_var.led_lamp_level = 3; System_setup_var.Autogestione_due_state = 1; } else { System_var.led_lamp_level = 4; } } else { if (aADCxConvertedData[AN_VBAT_enum] < (12.60 / 0.00415) || System_setup_var.Autogestione_due_state == 3) { System_var.led_lamp_level = 1; System_setup_var.Autogestione_due_state = 3; } else if (aADCxConvertedData[AN_VBAT_enum] < (12.80 / 0.00415) || System_setup_var.Autogestione_due_state == 2) { System_var.led_lamp_level = 2; System_setup_var.Autogestione_due_state = 2; } else if (aADCxConvertedData[AN_VBAT_enum] < (13.00 / 0.00415) || System_setup_var.Autogestione_due_state == 1) { System_var.led_lamp_level = 3; System_setup_var.Autogestione_due_state = 1; } else { System_var.led_lamp_level = 4; } } } //---------------- System_Autogestione_uno_Load() ---------------------------------------------- void System_Midnight_uno_Load() { float fasce_orarie = ((System_setup_var.last_night_ore > 3) ? (float) System_setup_var.last_night_ore : 12) / 2.; if (SystemFlags.Battery_filled) { if (System_setup_var.Midnight_uno_deltafasce[1] >= 0.25) { System_setup_var.delta_inc_dec_load = 0.25; } } else { if (System_setup_var.Midnight_uno_deltafasce[0] >= 0.25) { System_setup_var.delta_inc_dec_load = -0.25; } } System_setup_var.Midnight_uno_deltafasce[0] += System_setup_var.delta_inc_dec_load; System_setup_var.Midnight_uno_deltafasce[1] -= System_setup_var.delta_inc_dec_load; System_setup_var.Midnight_uno_deltafasce[2] += System_setup_var.delta_inc_dec_load; System_setup_var.Midnight_uno_duratafasce[0] = fasce_orarie * (System_setup_var.Midnight_uno_deltafasce[0] / 2.); System_setup_var.Midnight_uno_duratafasce[1] = fasce_orarie * System_setup_var.Midnight_uno_deltafasce[1]; System_setup_var.Midnight_uno_duratafasce[2] = fasce_orarie * (System_setup_var.Midnight_uno_deltafasce[2] / 2.); } //---------------- System_Autogestione_uno_Load() ---------------------------------------------- void System_Midnight_due_Load() { float fasce_orarie = ((System_setup_var.last_night_ore > 3) ? (float) System_setup_var.last_night_ore : 12.) / 7.; if (SystemFlags.Battery_filled) { if (System_setup_var.Midnight_due_tabselect < 15) System_setup_var.Midnight_due_tabselect++; } else { if (System_setup_var.Midnight_due_tabselect > 0) System_setup_var.Midnight_due_tabselect--; } for (uint8_t i = 0; i < 7; i++) System_setup_var.Midnight_due_duratafasce[i] = fasce_orarie * Night_due_tab_pot[System_setup_var.Midnight_due_tabselect][i]; } void System_SunnyOne_mode_Load(){ // Gestisce la metodologia ON_DEPENDING_PROD bool save_energy = true; if (SystemFlags.Battery_filled) save_energy = false; float local_tot_energy_bit = SunnyOne_mode_Load.WhPan_on_day_bit * ((save_energy ? SunnyOne_mode_Load.scarto_in_salita : SunnyOne_mode_Load.scarto_in_discesa )); float temp_hour_energy_bit = (local_tot_energy_bit / ((System_setup_var.last_night_ore > 3) ? System_setup_var.last_night_ore : 10));// 3.37 rapporto tra IBAT e ILED --> Con il nuovo approccio non serve for (uint8_t i = 0; i < (sizeof(SunnyOne_mode_Load.Energia_per_fascia_oraria_bit)/4); i++){ // impongo il minimo di 2W per fascia oraria SunnyOne_mode_Load.Energia_per_fascia_oraria_bit[i] = (temp_hour_energy_bit > (SINGLE_WH_PV_PROD_BIT*2)) ? (uint32_t) temp_hour_energy_bit : SINGLE_WH_PV_PROD_BIT*2; } SunnyOne_mode_Load.for_debug_tot_en = local_tot_energy_bit / 1000; // Per farlo rientrare in un uint16_t SunnyOne_mode_Load.for_debug_single_fascia_en = temp_hour_energy_bit / 1000; // Per farlo rientrare in un uint16_t } void System_applicacation_sel_level_lamp(uint8_t level) { float local_max_iLed = (float) System_setup_var.Setup_Max_ImA_Lamp * 1.081; // ??? convertito in bit ? if (System_setup_var.flags.testmode_lamp){ if (!System_setup_var.flags.bi_color_lamp) IR_ENABLE; // Sulla bi colore l'IR non è montato e comanda invece la selezione colore EN_LED_GPIO_Port->BSRR = EN_LED_Pin; //Accendo Mos Led } else { if (System_setup_var.Setup_LedMode == ON_DEPENDING_PROD) { // Sunny ONE if (level == 0){ System_var.iLed_set_to_reach_mA_bit = 0; SystemFlags.lamp_on = 0; if (System_setup_var.flags.bi_color_lamp) IR_DISABLE; // Se bicolore spengo il giallo SystemTimer.time_led_on_timer_ms = 0; } else { if (SunnyOne_mode_Load.Energia_per_fascia_oraria_bit[level - 1] == 0){ System_var.iLed_set_to_reach_mA_bit = 0; SystemFlags.lamp_on = 0; if (System_setup_var.flags.bi_color_lamp) IR_DISABLE; // Se bicolore spengo il giallo } else { SystemFlags.lamp_on = 1; if (System_var.iLed_set_to_reach_mA_bit == 0){ System_var.iLed_set_to_reach_mA_bit = SunnyOne_mode_Load.Energia_per_fascia_oraria_bit[level - 1] / MAX_VLED_VOLTAGE_BIT; } else { /* Modalità fissa */ System_var.iLed_set_to_reach_mA_bit = SunnyOne_mode_Load.Energia_per_fascia_oraria_bit[level - 1] / aADCxConvertedData[AN_VLED_enum]; } if (System_var.iLed_set_to_reach_mA_bit > local_max_iLed) System_var.iLed_set_to_reach_mA_bit = local_max_iLed; } SunnyOne_mode_Load.for_debug_single_fascia_en = SunnyOne_mode_Load.Energia_per_fascia_oraria_bit[level - 1] / 1000; // Per farlo rientrare in un uint16_t if (SystemFlags.lamp_on) EN_LED_GPIO_Port->BSRR = EN_LED_Pin; //Accendo Mos Led } } else { if (level != 0) { if (!System_setup_var.flags.bi_color_lamp) IR_ENABLE; // Sulla bi colore l'IR non è montato e comanda invece la selezione colore EN_LED_GPIO_Port->BSRR = EN_LED_Pin; //Accendo Mos Led } else { if (!System_setup_var.flags.bi_color_lamp) IR_DISABLE; } if (SystemFlags.Presence_sensor && SystemTimer.IR_presence_timer_ms > 0) level = 4; else SystemFlags.Presence_sensor = 0; switch (level) { case 0: System_var.iLed_set_to_reach_mA_bit = 0; SystemFlags.lamp_on = 0; if (System_setup_var.flags.bi_color_lamp) IR_DISABLE; // Se bicolore spengo il giallo // EN_LED_GPIO_Port->BRR = EN_LED_Pin; // Spengo Mos Led --> Spegnere qui il mos non garantische che la lampada sia a PWM 0,pericolo corrente inversa, viene fatto nel system application led timed SystemTimer.time_led_on_timer_ms = 0; break; case 1: System_var.iLed_set_to_reach_mA_bit = local_max_iLed * 0.25; break; case 2: System_var.iLed_set_to_reach_mA_bit = local_max_iLed * 0.5; break; case 3: System_var.iLed_set_to_reach_mA_bit = local_max_iLed * 0.75; break; case 4: System_var.iLed_set_to_reach_mA_bit = local_max_iLed * 0.95; break; default: System_var.iLed_set_to_reach_mA_bit = local_max_iLed * 0.5; break; } } } } void System_application_gestione_lampada(void) { if (((SystemFlags.lowLowBattery || SystemFlags.lowBattery) && !SystemFlags.ups_found) || SystemFlags.overload || SystemFlags.overtemperature || SystemFlags.overBattemperature) { // || SystemFlags.lowBattemperature non serve spegnere la lampada in questo caso System_var.iLed_set_to_reach_mA_bit = 0; SystemFlags.lamp_on = 0; // EN_LED_GPIO_Port->BRR = EN_LED_Pin; // Spengo Mos Led --> Spegnere qui il mos non garantische che la lampada sia a PWM 0, viene fatto nel system application led timed SystemTimer.time_led_on_timer_ms = 0; } else { uint32_t T_ore_led_on = SystemTimer.time_led_on_timer_ms / (1000 * 60 * 60); if (System_setup_var.flags.bi_color_lamp) { if (!System_setup_var.flags.testmode_lamp){ #ifdef FIERA_MODE if (System_setup_var.Setup_LedMode != ON_AT_DAY) { //solo per fiera #endif if (T_ore_led_on < ((System_setup_var.bi_color_lamp_auto_switch_hour == 0) ? System_setup_var.last_night_ore/2 : System_setup_var.bi_color_lamp_auto_switch_hour) || SystemFlags.lamp_on == 0) { System_setup_var.flags.color_lamp_yellow_white = 0; } else { System_setup_var.flags.color_lamp_yellow_white = 1; } #ifdef FIERA_MODE } #endif } if (System_setup_var.flags.color_lamp_yellow_white){ IR_ENABLE; // Non ha senso controllare il bit invece che usarlo direttamente --> if (enIR_GPIO_Port->ODR & enIR_Pin) superfluo } else { IR_DISABLE; } } if (System_setup_var.flags.testmode_lamp){ if (System_var.iLed_set_to_reach_mA_bit != 0) SystemFlags.lamp_on = 1; else SystemFlags.lamp_on = 0; } else { switch (System_setup_var.Setup_LedMode) { case ALWAYS_ON: // Carico sempre acceso SystemFlags.lamp_on = 1; System_var.led_lamp_level = 4; break; case AFTER_SUNSET_1H ... AFTER_SUNSET_16H: if (SystemFlags.day) { // di giorno SystemFlags.lamp_on = 0; // il carico è spento System_var.led_lamp_level = 0; } else { if (T_ore_led_on < System_setup_var.Setup_LedMode) { SystemFlags.lamp_on = 1; // Accende il carico System_var.led_lamp_level = 4; } else { SystemFlags.lamp_on = 0; // Spegne il carico System_var.led_lamp_level = 0; } } break; case ON_AT_NIGHT: // carico acceso di notte if (SystemFlags.day) { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } else { SystemFlags.lamp_on = 1; System_var.led_lamp_level = 4; } break; case ON_AT_DAY: // Carico acceso solo di giorno #ifdef FIERA_MODE // Solo per fiera per far ciclare la lampada uint32_t T_minuti_led_on = SystemTimer.timer_counter_per_ciclo_fiera / (1000 * 60); if (T_minuti_led_on % 3 < 1) { SystemFlags.lamp_on = 1; System_var.led_lamp_level = 4; System_setup_var.flags.color_lamp_yellow_white = 0; } else if (T_minuti_led_on % 3 < 2){ SystemFlags.lamp_on = 1; System_var.led_lamp_level = 4; System_setup_var.flags.color_lamp_yellow_white = 1; } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; System_setup_var.flags.color_lamp_yellow_white = 0; } #else // Originale if (SystemFlags.day) { SystemFlags.lamp_on = 1; System_var.led_lamp_level = 4; } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } #endif break; case AUTO1: // Autogestione Uno if (!SystemFlags.day) { if (T_ore_led_on < 12) { SystemFlags.lamp_on = 1; if (T_ore_led_on < System_setup_var.Autogestione_uno_soglia_ora) System_var.led_lamp_level = 4; else System_var.led_lamp_level = 1; } } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } break; case ON_DEPENDING_VBAT: // Autogestione due a soglie Vbat if (!SystemFlags.day) { SystemFlags.lamp_on = 1; System_Autogestione_due_Load(); } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } break; case MIDNIGHT_1: // Midnight uno if (!SystemFlags.day) { SystemFlags.lamp_on = 1; if (T_ore_led_on < System_setup_var.Midnight_uno_duratafasce[0]) System_var.led_lamp_level = 1; else if (T_ore_led_on < (System_setup_var.Midnight_uno_duratafasce[0] + System_setup_var.Midnight_uno_duratafasce[1])) System_var.led_lamp_level = 4; else if (T_ore_led_on > (System_setup_var.Midnight_uno_duratafasce[0] + System_setup_var.Midnight_uno_duratafasce[1])) System_var.led_lamp_level = 1; else System_var.led_lamp_level = 0; // Qui non ci cade mai } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } break; case MIDNIGHT_2: // Midnight due if (!SystemFlags.day) { SystemFlags.lamp_on = 1; uint8_t tmp_fascia = 0; uint16_t tmp_sum = 0; for (tmp_fascia = 0; tmp_fascia < 7; tmp_fascia++) { tmp_sum += System_setup_var.Midnight_due_duratafasce[tmp_fascia]; if (T_ore_led_on < tmp_sum) break; } switch (tmp_fascia) { case 0: case 6: System_var.led_lamp_level = 1; break; case 1: case 5: System_var.led_lamp_level = 2; break; case 2: case 4: System_var.led_lamp_level = 3; break; case 3: System_var.led_lamp_level = 4; break; } } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } break; case USER_SETTING: // segue le impostazioni utente // // Sempre 3 bit per fascia oraria , ma 0 == spenta 1-4 == livelli dal più basso al più alto if (!SystemFlags.day) { SystemFlags.lamp_on = 1; uint8_t tmp_state = (System_setup_var.gUser_load_mode_hours_set & ((uint64_t) 0x07 << (T_ore_led_on * 3))) >> (T_ore_led_on * 3); if (tmp_state == 0) SystemFlags.lamp_on = 0; System_var.led_lamp_level = tmp_state; } break; case ON_DEPENDING_PROD: // SUNNYONE MODE if (!SystemFlags.day) { System_var.led_lamp_level = T_ore_led_on + 1; // passo la fascia oraria a cui fare riferimento altrimenti dovrei cambiare il tipo di ingresso alla funzione SystemFlags.lamp_on = 1; } else { SystemFlags.lamp_on = 0; System_var.led_lamp_level = 0; } break; default: // ogni altra configurazione non è ammessa quindi imposta i ldefault System_setup_var.Setup_LedMode = AUTO1; break; } } System_applicacation_sel_level_lamp(System_var.led_lamp_level); } } // Gestione del carico LED // Anche questa chiamata a fine conversione ADC così da essere sincrona con le letture void System_application_led_timed(void) { // bit conversione 0.02 if (aADCxConvertedData[AN_VLED_enum] > MAX_VLED_VOLTAGE_BIT || System_var.iLed_set_to_reach_mA_bit == 0) { // 56V if (System_var.Led_PWM_bit != 0) { System_var.Led_PWM_bit = 0; System_var.Led_PWM_ditter_bit = 0; UpdateDitherTable((uint16_t*) &LedDitherTable, (uint16_t) (System_var.Led_PWM_ditter_bit >> 2), 0); PWM_Charge_Set_Duty(System_var.Led_PWM_bit, (uint32_t*)&htim16.Instance->CCR1); System_var.iLed_set_to_reach_mA_bit = 0; System_var.iLed_set_to_reach_mA = 0; EN_LED_GPIO_Port->BRR = EN_LED_Pin; // Spengo Mos Led if (aADCxConvertedData[AN_VLED_enum] > MAX_VLED_VOLTAGE_BIT) System_set_error_flag(ER_OVERVOLTLOAD); // Sostituisce -> SystemFlags.overVoltageload = 1; } } else { System_var.iLed_calc_mA_bit = (uint16_t) ((((uint64_t) aADCxConvertedData[AN_VBAT_enum] * aADCxConvertedData[AN_ILED_enum]) / (float) (aADCxConvertedData[AN_VLED_enum] + 1)) * coef_conversion.var_ILAMP_CALC_mAMPERE_COEF_BIT); // SystemFlags.overVoltageload = 0; --> Viene resettato nella gestione flag errori if (System_var.iLed_calc_mA_bit < System_var.iLed_set_to_reach_mA_bit) { // || System_var.iLed_calc_mA_bit > aADCxConvertedData[AN_ILED_enum] da capire a cosa serviva if (System_var.Led_PWM_ditter_bit < ((PWM_LED_COUNTER_PERIOD - 56) * 4)) { System_var.Led_PWM_ditter_bit++; // if (System_var.Led_PWM_bit < (PWM_LED_COUNTER_PERIOD - 56)) // System_var.Led_PWM_bit++; } } else if (System_var.iLed_calc_mA_bit > System_var.iLed_set_to_reach_mA_bit) { if (System_var.Led_PWM_ditter_bit > 0) System_var.Led_PWM_ditter_bit--; // if (System_var.Led_PWM_bit > 0) // System_var.Led_PWM_bit--; } System_var.Led_PWM_bit = System_var.Led_PWM_ditter_bit >> 2; UpdateDitherTable((uint16_t*) &LedDitherTable, (uint16_t) (System_var.Led_PWM_ditter_bit >> 2), (uint8_t) (System_var.Led_PWM_ditter_bit % 4)); // PWM_Charge_Set_Duty(System_var.Led_PWM_bit, &htim16.Instance->CCR1); } } void System_delay_active_UPS(void) { tasks_to_exec_list.Check_VPAN_Tasks = 1; HAL_GPIO_WritePin(enVDRV_GPIO_Port, enVDRV_Pin, GPIO_PIN_SET); //Attivo alimentazione Vdrive (in questa posizione rispetto a Start PWM per garantire la vdrv a 12v all'avvio del pwm (160uS)) if (tasks_to_exec_list.system_pwm_Tasks == 0 && SystemFlags.ups_rele) { Valuate_start_Photovoltaic_PWM_Value(SystemFlags.ups_rele ? true : false); SystemTimer.start_window_timer_ms = 50; if (htim1.State == HAL_TIM_STATE_READY) Start_Photovoltaic_PWM(); tasks_to_exec_list.system_pwm_Tasks = 1; } } void System_active_UPS(bool active) { if (active && System_setup_var.flags.ups_disable == 0) { SystemTimer.delayed_UPS_start = 2000; tasks_to_exec_list.system_pwm_Tasks = 0; Stop_Photovoltaic_PWM(); SystemFlags.ups_rele = 1; tasks_to_exec_list.Check_VPAN_Tasks = 1; tasks_to_exec_list.MPPT_Tasks = 0; System_mppt.flags.do_mppt_or_just_update = 0; UPS_Rele_GPIO_Port->BSRR = UPS_Rele_Pin; // Accendo rele //finestra per cui una volta attivato, se viene a mancare la tensione segnifica che l'ups non è in grado di reggere il carico e quindi si disattiva SystemTimer.find_under_voltage_ups_error_timer_ms = FIND_UNDER_VOLT_UPS_ERROR_PERIOD_MS; } else { // SystemTimer.delayed_UPS_start = 2000; tasks_to_exec_list.system_pwm_Tasks = 0; Stop_Photovoltaic_PWM(); SystemFlags.ups_rele = 0; tasks_to_exec_list.Check_VPAN_Tasks = 1; UPS_Rele_GPIO_Port->BRR = UPS_Rele_Pin; // Spengo Rele // System_var.operating_mode = 0; } } void System_application_Reflesh_bit_conversion(void) { System_var.vEoCharge_bit = (System_setup_var.Setup_VEoc) / (coef_conversion.var_VBA_VOLTAGE_BIT * 10); System_var.VRestartCharge_bit = (System_setup_var.Setup_VRsC) / (coef_conversion.var_VBA_VOLTAGE_BIT * 10); // Valore di tensione di ripresa carica System_var.vLowBatt_bit = (System_setup_var.Setup_VLB) / (coef_conversion.var_VBA_VOLTAGE_BIT * 10); System_var.vExit_LowBatt_bit = System_setup_var.Setup_VELB / (coef_conversion.var_VBA_VOLTAGE_BIT * 10); System_var.iChargeMax_bit = (System_setup_var.Setup_iCharge_max / (coef_conversion.var_ICHARGE_AMPERE_BIT * 1000)) + System_var.iCharge_bias_bit; System_var.q_derating_temp = (float) (System_var.iChargeMax_bit >> 1) / 425.; // (80° - 95°) --> vedi X_int_temp_table[20 - 23] System_var.iCharge_bias_bit_isteresy_for_batt_fill = (System_setup_var.Setup_iCharge_Bias_for_batt_fill / (coef_conversion.var_ICHARGE_AMPERE_BIT * 1000)); System_var.vPanMax_bit = System_setup_var.Setup_VPanMax / (coef_conversion.var_VPAN_VOLTAGE_BIT * 10); System_var.vNight_bit = System_setup_var.Setup_Vnight / (coef_conversion.var_VPAN_VOLTAGE_BIT * 10); // Valore di tensione pannello che fa passare in modalità notte System_var.vOvervoltage_bit = System_setup_var.Setup_VOvBatt / (coef_conversion.var_VBA_VOLTAGE_BIT * 10); if (System_setup_var.flags.pv_production_stop == 1) { Stop_Photovoltaic_PWM(); SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; } System_setup_var.crc = CRC_MODBUS16FastBlock(0xFFFF, sizeof(System_setup_var_t) - 2, (uint8_t*) &System_setup_var.Setup_Max_ImA_Lamp); tasks_to_exec_list.save_setup_Task = 1; // Forzo salvataggio dei settings --> ToDO far diventare una prenotazione } void System_application_check_state(void) { // Eseguito ogni 100 mS // Check in corrente se attivare oppure no il MOSPV if (aADCxConvertedData[AN_ICHG_enum] > System_var.iCharge_bias_bit + (21*6) ){ /* 21 = circa 100mA*/ MOS_PV_ON; SystemFlags.mosPV = 1; } else if (aADCxConvertedData[AN_ICHG_enum] < System_var.iCharge_bias_bit + (21*3)) { MOS_PV_OFF; SystemFlags.mosPV = 0; } // Check tensione Batteria if (aADCxConvertedData[AN_VBAT_enum] < (System_setup_var.Setup_VLB - 4) / (coef_conversion.var_VBA_VOLTAGE_BIT * 10)) { // Se Vbat < 3V rispetto il suo VLB SystemFlags.lowLowBattery = 1; } else { SystemFlags.lowLowBattery = 0; } if (aADCxConvertedData[AN_VBAT_enum] < (System_setup_var.Setup_VLB - 30) / (coef_conversion.var_VBA_VOLTAGE_BIT * 10)) { // Se Vbat < 3V rispetto il suo VLB SystemFlags.battery_found = 0; } else { SystemFlags.battery_found = 1; } if (SystemFlags.lowBattery == 0 && aADCxConvertedData[AN_VBAT_enum] < System_var.vLowBatt_bit && SystemFlags.battery_found) { // Led_on_off_blink(LED2, BLINK, 200); SystemFlags.lowBattery = 1; System_setup_var.NLowBattery++; } if (SystemFlags.lowBattery == 1 && aADCxConvertedData[AN_VBAT_enum] > System_var.vExit_LowBatt_bit && SystemFlags.battery_found) { // Led_on_off_blink(LED2, OFF, 0); SystemFlags.lowBattery = 0; if (System_setup_var.Setup_LedMode == ALWAYS_ON && SystemFlags.ups_rele){ System_active_UPS(false); ups_undervoltage_recursive_error = 0; } } if (System_setup_var.flags.ups_mode_ext_control) { if (SystemFlags.ups_rele == 1 && System_setup_var.flags.ups_force_from_ext == 0) System_active_UPS(false); else if (System_setup_var.flags.ups_force_from_ext && SystemFlags.ups_found && SystemFlags.ups_rele == 0) System_active_UPS(true); } else { if (System_setup_var.flags.ups_mode_ups_sustain && SystemFlags.lamp_on && SystemFlags.ups_found && SystemFlags.ups_rele == 0 && aADCxConvertedData[AN_VBAT_enum] < (System_var.vEoCharge_bit - 250 /* circa 1V in bit*/)) // Aggiungere soglia di attivazione System_active_UPS(true); // Considerare che una volta entrato in ups si disattiva solo quando la lampada si spegnerà -- Quindi se lampada always on non ne esce più, discuterne con i colleghi // Attivazione dell'alimentazione da parte dell'UPS se presente - Scollegato balla rilevazione del lowbattery if (SystemFlags.lowBattery == 1 && SystemFlags.lamp_on && SystemFlags.ups_found && SystemFlags.ups_rele == 0) { System_active_UPS(true); } // if (System_var.operating_mode == 1 && SystemFlags.ups_rele == 0) { // System_active_UPS(true); // } } // Check di produzione // Da non fare se in UPS if (SystemFlags.Photovoltaic_pwm_active && icharger_battery_current_copy <= (System_var.iCharge_bias_bit + System_var.iCharge_bias_bit_isteresy_for_batt_fill) && SystemFlags.ups_rele == 0) { if (SystemTimer.check_pv_production_timer_ms <= 0) { SystemTimer.check_pv_production_timer_ms = CHECK_PV_PRODUCTION_PERIOD_MS; Stop_Photovoltaic_PWM(); if (SystemTimer.battery_full_delay_timer_ms > BATTERY_FULL_DELAY_MS - 50) { // if (SystemFlags.Battery_filled == 1) //può rientrarci più volte ma deve essere disabilitata la carica solo la prima volta SystemFlags.battery_can_charge = 0; // Batteria non può essere caricata SystemFlags.Battery_filled = 1; // Batteria caricata totalmente durante il giorno - usato per autogestione load SystemTimer.battery_full_delay_timer_ms = 0; } SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; } } else { SystemTimer.check_pv_production_timer_ms = CHECK_PV_PRODUCTION_PERIOD_MS; // SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; } // Compensazione fine carica per Piombo // System_setup_var.flags.battery_type_Pb_Li = 1; if (System_setup_var.flags.battery_type_Pb_Li) { uint8_t t_index = 0; while (aADCxConvertedData[AN_TBAT_enum] < X_bat_temp_table[t_index]) t_index++; if (t_index < 2) t_index = 0; if (t_index > 16) t_index = 16; // Check temperatura Batteria if (aADCxConvertedData[AN_TBAT_enum] > 4030){ t_index = 8; // Suppongo che la sonda di temperatura è scollegata System_set_error_flag(ER_BAT_TEMP_DISCONNECTED); } else if (aADCxConvertedData[AN_TBAT_enum] > X_bat_temp_table[4 + (SystemFlags.lowBattemperature ? 1 : 0)] && System_setup_var.flags.battery_type_Pb_Li == 0) { // Con isteresi in caso di errore System_set_error_flag(ER_LOW_BAT_TEMP); } System_var.vEoC_delta_Flood_bit = V_Tcomp_VoeC_Pb[t_index - 2] / (coef_conversion.var_VBA_VOLTAGE_BIT); if (SystemFlags.Battery_filled) System_var.vEoC_delta_Flood_bit -= (1.2 / (coef_conversion.var_VBA_VOLTAGE_BIT)); // valore da impostare per abbassare la tensione del flood) } else System_var.vEoC_delta_Flood_bit = 0; // Compensazione in temperatura // calcolo del derating in Temperatura sulla corrente massima gestibile dal regolatore System_var.derating_temp = 0; if (aADCxConvertedData[AN_TMOS_enum] < X_int_temp_table[20]) { if (aADCxConvertedData[AN_TMOS_enum] < X_int_temp_table[23 - (SystemFlags.overtemperature ? 2 : 0)]) { // In questo modo ho un Delta una volta entrata nell'errore System_var.derating_temp = System_var.iChargeMax_bit; System_set_error_flag(ER_OVERTEMP); Stop_Photovoltaic_PWM(); // TO DO gli overvoltage SystemTimer.check_voltage_pan_timer_ms = CHECK_VPAN_TASK_PERIOD_MS; tasks_to_exec_list.Check_VPAN_Tasks = 1; } else { System_var.derating_temp = (X_int_temp_table[20] - aADCxConvertedData[AN_TMOS_enum]) * System_var.q_derating_temp; } } } void System_set_error_flag(er_enum_t error_flag) { if (((SystemFlags.sub_16_flag_0 >> error_flag) & 0x01) == 0) { // Errore non presente guindi aggiungo SystemFlags.sub_16_flag_0 |= (1 << (uint8_t) error_flag); SystemTimer.Error_hysteresis_timer_ms.N_count_active++; SystemTimer.Error_hysteresis_timer_ms.link_error[SystemTimer.Error_hysteresis_timer_ms.N_count_active - 1] = error_flag; SystemTimer.Error_hysteresis_timer_ms.counter[SystemTimer.Error_hysteresis_timer_ms.N_count_active - 1] = 30000; SystemTimer.Error_repeat_event_timer_ms.N_error_event_in_a_day[error_flag]++; // Contatore ripetizione eventi, viene resettato all'alba (Day--> ON) } else { // Errore già presente, solo da riavviare il contatore for (uint8_t i = 0; i < SystemTimer.Error_hysteresis_timer_ms.N_count_active; i++) { // Cerco il contatore relativo all'errore if (SystemTimer.Error_hysteresis_timer_ms.link_error[i] == error_flag) { SystemTimer.Error_hysteresis_timer_ms.counter[i] = 30000; break; } } } Led_on_off_blink(LED2, BLINK, 300); } void System_check_error_flags_to_reset(void) { for (uint8_t i = 0; i < SystemTimer.Error_hysteresis_timer_ms.N_count_active; i++) { // Cerco il contatore relativo all'errore da resettare if (SystemTimer.Error_hysteresis_timer_ms.counter[i] <= 0) { if (SystemTimer.Error_repeat_event_timer_ms.N_error_event_in_a_day[SystemTimer.Error_hysteresis_timer_ms.link_error[i]] < 5 && i < 4) SystemFlags.sub_16_flag_0 &= ~((uint16_t) 0x1 << SystemTimer.Error_hysteresis_timer_ms.link_error[i]); SystemTimer.Error_hysteresis_timer_ms.link_error[i] = 0xFF; SystemTimer.Error_hysteresis_timer_ms.counter[i] = -1; SystemTimer.Error_hysteresis_timer_ms.N_count_active--; if (i == SystemTimer.Error_hysteresis_timer_ms.N_count_active) { // Vuol dire che era l'ultimo in lista non devo far nulla } else { // Devo fa sciftare tutti gli errori da questa posizione in poi di uno in meno for (uint8_t z = i; z < SystemTimer.Error_hysteresis_timer_ms.N_count_active; z++) { SystemTimer.Error_hysteresis_timer_ms.link_error[z] = SystemTimer.Error_hysteresis_timer_ms.link_error[z + 1]; SystemTimer.Error_hysteresis_timer_ms.counter[z] = SystemTimer.Error_hysteresis_timer_ms.counter[z + 1]; } } break; } } Led_on_off_blink(LED2, OFF, 0); // if (SystemTimer.Error_hysteresis_timer_ms == 0) { // SystemTimer.Error_hysteresis_timer_ms = -1; // SystemFlags.sub_16_flag_0 &= 0xFC00; // } } //float WhLoad_bit = 0; void System_Energy_counter_calc(void) { System_Energy_calc_var.counter++; System_Energy_calc_var.WhLoad += aADCxConvertedData[AN_VBAT_enum] * (aADCxConvertedData[AN_ILED_enum] > 10 ? aADCxConvertedData[AN_ILED_enum] : 0); int32_t local_an_ichg = aADCxConvertedData[AN_ICHG_enum] - System_var.iCharge_bias_bit; if (local_an_ichg < 10) local_an_ichg = 0; if (SystemFlags.ups_rele) { System_Energy_calc_var.WhPan += 0; System_Energy_calc_var.WhUps += aADCxConvertedData[AN_VBAT_enum] * local_an_ichg; } else { System_Energy_calc_var.WhPan += aADCxConvertedData[AN_VBAT_enum] * local_an_ichg; System_Energy_calc_var.WhUps += 0; } if (System_Energy_calc_var.counter > 60) { // ogni 60 secondi System_Energy_calc_var.counter = 0; float WhPan_step = (System_Energy_calc_var.WhPan * coef_conversion.var_ICHARGE_AMPERE_BIT * coef_conversion.var_VBA_VOLTAGE_BIT) / 3600.; float WhLoad_step = (System_Energy_calc_var.WhLoad * (coef_conversion.var_ILAMP_mAMPERE_BIT) * coef_conversion.var_VBA_VOLTAGE_BIT) / 3600.; float WhUps_step = (System_Energy_calc_var.WhUps * coef_conversion.var_ICHARGE_AMPERE_BIT * coef_conversion.var_VBA_VOLTAGE_BIT) / 3600.; System_setup_var.WhLoad += WhLoad_step; System_setup_var.WhPan += WhPan_step; System_setup_var.WhUps += WhUps_step; SunnyOne_mode_Load.WhPan_on_day += WhPan_step; SunnyOne_mode_Load.WhPan_on_day_bit += System_Energy_calc_var.WhPan/ 3600.; // WhLoad_bit += System_Energy_calc_var.WhLoad / 3600.; if ((WhPan_step + WhUps_step) > WhLoad_step) { System_setup_var.WhInBatt += (WhPan_step + WhUps_step - WhLoad_step); } else { System_setup_var.WhOutBatt += (WhLoad_step - (WhPan_step + WhUps_step)); } System_Energy_calc_var.WhLoad = 0; System_Energy_calc_var.WhPan = 0; System_Energy_calc_var.WhUps = 0; System_Energy_calc_var.ContatoreMinuti++; if (System_Energy_calc_var.ContatoreMinuti >= 60) { // ogni ora System_Energy_calc_var.ContatoreMinuti = 0; System_setup_var.NContaOre++; } } } void PWM_Charge_Set_Duty(uint32_t duty, uint32_t *reg) { *reg = duty; } int16_t System_temp_calc(System_tempe_table_t *table, uint16_t adc_value) { uint16_t index = 0; int16_t temperatura = 0; // 0.1 °C float q_interpol = 0; while (adc_value > table->p_table_x[index] && index < (table->dim - 1)) index++; if (index == 0) return -10; q_interpol = (table->p_table_x[index - 1] - adc_value) / (table->p_table_x[index - 1] - table->p_table_x[index]); float y_out = (float) table->p_table_y[index] - ((float) (table->p_table_y[index - 1] - table->p_table_y[index]) * q_interpol); temperatura = y_out * 10; return temperatura; } void System_gestione_ext_led(void) { // Descrizione stati LED esterni /* * 0x00 Tutto Spento * 0x10 Blink Verde * 0x20 Blink Giallo * 0x30 Blink Rosso * 0x11 ON Verde * 0x22 ON Giallo * 0x33 On Rosso * * LED ROSSO Blink LED VERDE Blink LED GIALLO Blink LED DI PAUSA Fisso STATO 1 + N° errore Spento ROSSO Sistema in allarme, con allarme N° - 1 blink led Spento 1 + NxA PV NxA Led Verde/Giallo Rilevato Giorno. Verde in pausa produzione da PV, giallo da UPS Spento NxA PV 1 +NxA Led Verde/Giallo Rilevata Notte. Verde in pausa produzione da PV, giallo da UPS ------- -------- -------- Giallo/Verde Se durante lo stato pausa il led cambia di colore significa che si sta a vendo un derating dovuto alla temperatura */ led_tabs_array.counter = 0; led_tabs_array.total_step = 0; led_tabs_array.timed_state[led_tabs_array.total_step++] = SystemFlags.day ? LED_EXT_BLINK_GREEN : LED_EXT_BLINK_YELLOW; // ------------ Stato Fisso ad indicare pv o ups ------ // memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_OFF, LED_EXT_SEP_TIME); // pausa tra i due blink // led_tabs_array.total_step += LED_EXT_SEP_TIME; led_tabs_array.timed_state[led_tabs_array.total_step++] = SystemFlags.ups_rele ? LED_EXT_ON_YELLOW : LED_EXT_ON_GREEN; // ------------ Produzione ------ uint8_t amp_prod = 0; if (aADCxConvertedData[AN_ICHG_enum] > coef_conversion.var_ICHARGE_BIAS_BIT) amp_prod = (aADCxConvertedData[AN_ICHG_enum] - coef_conversion.var_ICHARGE_BIAS_BIT) / 188; // 210 circa 1A if (amp_prod > 10) amp_prod = 10; if (amp_prod != 0 ) { memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_OFF, LED_EXT_SEP_TIME); // pausa tra i due blink led_tabs_array.total_step += LED_EXT_SEP_TIME; memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_BLINK_GREEN, amp_prod); led_tabs_array.total_step += amp_prod; } // ------------ SCarica ------ uint8_t amp_load = aADCxConvertedData[AN_ILED_enum] / 631; if (amp_load > 15) amp_load = 15; if (amp_load != 0) { memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_OFF, LED_EXT_SEP_TIME); // pausa tra i due blink led_tabs_array.total_step += LED_EXT_SEP_TIME; memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_BLINK_YELLOW, amp_load); led_tabs_array.total_step += amp_load; } // Low_battery e derating if (SystemFlags.lowBattery || aADCxConvertedData[AN_TMOS_enum] < X_int_temp_table[20] || SystemFlags.sub_16_flag_0 != 0){ memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_OFF, LED_EXT_SEP_TIME); // pausa tra i due blink led_tabs_array.total_step += LED_EXT_SEP_TIME; } if (SystemFlags.sub_16_flag_0 != 0) { /* ERRORE DA MOSTRARE Individuare errore e quindi settare la tabella da utilizzare unsigned inverse_current : 1; unsigned overload : 1; unsigned overvoltage : 1; unsigned overVoltageload : 1; unsigned overVoltagePV : 1; unsigned overVoltageUps : 1; unsigned overtemperature : 1; unsigned lowBattemperature : 1; unsigned overBattemperature : 1; unsigned fash_error : 1; unsigned flag_0_empty : 6; */ uint8_t local_err = 0; // (SystemFlags.inverse_current ? 1 : 0); while (((SystemFlags.sub_16_flag_0 >> local_err) & 0x01) == 0 && local_err < 12) local_err++; local_err++; // Perchè parte da zero - tengo separata dalla somma successiva per lettura // if (local_err > 13) // local_err = 0; local_err += 2; // per comprendere le notifiche di low battery e derating memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_BLINK_RED, local_err); led_tabs_array.total_step += local_err; } else if (SystemFlags.lowBattery){// Se low battery aggiungo un blink rosso led_tabs_array.timed_state[led_tabs_array.total_step++] = LED_EXT_BLINK_RED; } else if (aADCxConvertedData[AN_TMOS_enum] < X_int_temp_table[20]){ // Se in derating in temperatura aggiungo un blink rosso led_tabs_array.timed_state[led_tabs_array.total_step++] = LED_EXT_BLINK_RED; // Costretto a fare così perché cmq deve avere più importanza il low battery rispetto al derating led_tabs_array.timed_state[led_tabs_array.total_step++] = LED_EXT_BLINK_RED; } memset(&led_tabs_array.timed_state[led_tabs_array.total_step], LED_EXT_OFF, LED_EXT_PAUSE_TIME); led_tabs_array.total_step += LED_EXT_PAUSE_TIME; } void System_gestione_ext_led_timed(void) { /* * 0: led spenti * 1: Verde Acceso * 2: Giallo Acceso * 3: Rosso Acceso */ uint8_t local_state = ((led_tabs_array.timed_state[led_tabs_array.counter / 2] >> ((led_tabs_array.counter % 2) * 4)) & 0x0F); switch (local_state) { case 0: LED_EXT_R_OFF; LED_EXT_V_OFF; break; case 1: LED_EXT_R_OFF; LED_EXT_V_ON; break; case 2: LED_EXT_R_ON; LED_EXT_V_ON; break; case 3: LED_EXT_R_ON; LED_EXT_V_OFF; break; default: LED_EXT_R_OFF; LED_EXT_V_OFF; break; } led_tabs_array.counter++; if (led_tabs_array.counter >= (led_tabs_array.total_step * 2)) { System_gestione_ext_led(); } } void System_evaluate_dark_pv_on_off(void) { if (SystemFlags.ups_rele){ if((aADCxConvertedData[AN_VPANP_enum] - aADCxConvertedData[AN_VPANM_enum]) < MIN_VPAN_VOLTAGE_START_PWM_BIT){ SystemFlags.evaluate_darkpv = 1; }else{ SystemFlags.evaluate_darkpv = 0; } }else{ if ((aADCxConvertedData[AN_ICHG_enum] - System_var.iCharge_bias_bit < 20) && (aADCxConvertedData[AN_VPANP_enum] - aADCxConvertedData[AN_VPANM_enum]) < MIN_VPAN_VOLTAGE_START_PWM_BIT){ SystemFlags.evaluate_darkpv = 1; }else{ SystemFlags.evaluate_darkpv = 0; } } SystemTimer.v_pv_to_store_on_variable_delay_ms = 20; }