void knklogic_retard(struct ecudata_t* d, retard_state_t* p_rs) { if (p_rs->delay_counter != 0) p_rs->delay_counter--; else { if (p_rs->knock_flag) { //detonation present d->knock_retard+= d->param.knock_retard_step;//retard p_rs->knock_flag = 0; } else {//detonation is absent d->knock_retard-= d->param.knock_advance_step;//advance } restrict_value_to(&d->knock_retard, 0, d->param.knock_max_retard); p_rs->delay_counter = d->param.knock_recovery_delay; if (0!=(p_rs->delay_counter)) --(p_rs->delay_counter); } }
void knklogic_retard(struct ecudata_t* d, retard_state_t* p_rs) { if (p_rs->knock_flag) { //detonation is present d->corr.knock_retard+= d->param.knock_retard_step;//retard p_rs->knock_flag = 0; p_rs->delay_counter = d->param.knock_recovery_delay; //reset delay } else { //detonation is absent if (p_rs->delay_counter == 0) { d->corr.knock_retard-= d->param.knock_advance_step;//advance p_rs->delay_counter = d->param.knock_recovery_delay; } } //restrict knock retard value restrict_value_to(&d->corr.knock_retard, 0, d->param.knock_max_retard); if (p_rs->delay_counter != 0) p_rs->delay_counter--; }
void choke_control(struct ecudata_t* d) { switch(chks.state) { case 0: //Initialization of choke position if (!IOCFG_CHECK(IOP_PWRRELAY)) //Skip initialization if power management is enabled initial_pos(INIT_POS_DIR, d->param.sm_steps); chks.state = 2; break; case 1: //system is being preparing to power-down initial_pos(INIT_POS_DIR, d->param.sm_steps); chks.state = 2; break; case 2: //wait while choke is being initialized if (!stpmot_is_busy()) //ready? { if (chks.pwdn) chks.state = 3; //ready to power-down else chks.state = 5; //normal working chks.smpos = 0; } break; case 3: //power-down if (pwrrelay_get_state()) { chks.pwdn = 0; chks.state = 5; } break; case 5: //normal working mode if (d->choke_testing) { initial_pos(INIT_POS_DIR, d->param.sm_steps); chks.state = 6; //start testing } else { int16_t tmp_pos = (((int32_t)d->param.sm_steps) * choke_closing_lookup(d)) / 200; int16_t rpm_cor = 0, diff; int16_t pos = tmp_pos + rpm_cor; restrict_value_to(&pos, 0, d->param.sm_steps); diff = pos - chks.smpos; if (!stpmot_is_busy() && diff != 0) { stpmot_dir(diff < 0 ? SM_DIR_CW : SM_DIR_CCW); stpmot_run(abs(diff)); //start stepper motor chks.smpos += diff; } } goto check_pwr; // Testing modes case 6: //initialization of choke if (!stpmot_is_busy()) //ready? { stpmot_dir(SM_DIR_CCW); stpmot_run(d->param.sm_steps); chks.state = 7; } goto check_tst; case 7: if (!stpmot_is_busy()) //ready? { stpmot_dir(SM_DIR_CW); stpmot_run(d->param.sm_steps); chks.state = 6; } goto check_tst; default: check_tst: if (!d->choke_testing) chks.state = 1; //exit choke testing mode check_pwr: if (!pwrrelay_get_state()) { //power-down chks.pwdn = 1; chks.state = 1; } break; } }
//Пропорциональный регулятор для регулирования оборотов ХХ углом опережения зажигания // Возвращает значение угла опережения в целом виде * 32. int16_t idling_pregulator(struct ecudata_t* d, volatile s_timer8_t* io_timer) { int16_t error,factor,iState_error; //зона "подхвата" регулятора при возвращени двигателя из рабочего режима в ХХ uint16_t capture_range = 100; //если PXX отключен или обороты значительно выше от нормальных холостых оборотов // или двигатель не прогрет то выходим с нулевой корректировкой if (!d->param.idl_regul || (d->sens.frequen >(d->param.idling_rpm + capture_range)) || (d->sens.temperat < d->param.idlreg_turn_on_temp && d->param.tmp_use)) return 0; //вычисляем целевые обороты , по массиву Обороты vs температура idl_coolant_rpm.output_state = idl_coolant_rpm_function(d)/16; user_var1 = idl_coolant_rpm.output_state; // Если обороты выше зоны РХХ , то включить задвигание РХХ if (d->sens.frequen >(idl_coolant_rpm.output_state + capture_range)) { idlregul_set_state(2,d); } //если PXX отключен или обороты значительно выше от нормальных холостых оборотов // или обороты не упали ниже целевые +10 первый раз после выхода с рабочего режима , то выходим с нулевой корректировкой //также обнуляем сумму интегрального регулятора при первом разрешенном входе в РХХ if ((d->sens.frequen >(idl_coolant_rpm.output_state + 10)) && !idl_enable.output_state) return 0; else {idl_enable.output_state = 1; iState_error = 0; } if (!d->param.idl_regul || (d->sens.frequen >(idl_coolant_rpm.output_state + capture_range))) return 0; //вычисляем значение ошибки, ограничиваем ошибку (если нужно), а также, если мы в зоне //нечувствительности, то выход с 0 коррекцией. error = idl_coolant_rpm.output_state - d->sens.frequen; restrict_value_to(&error, -200, 100); //Складываем ошибки для реализации интегрального регулятора iState_error += error; user_var2 = iState_error; if (abs(error) <= d->param.MINEFR) { // если в зоне нечувствительности , то обнуляем сумму интегрального регулятора iState_error = 0; return 0;//idl_prstate.output_state; } // тут работаем с механическим РХХ if (abs(error)<= (d->param.MINEFR +20)) idlregul_set_state(3,d); else if (error >0) idlregul_set_state(2,d); else idlregul_set_state(1,d); //выбираем необходимый коэффициент регулятора, в зависимости от знака ошибки //if (error > 0) factor = d->param.ifac1; //else // factor = d->param.ifac2; //изменяем значение коррекции только по таймеру idle_period_time_counter //if (s_timer_is_action(*io_timer)) { //s_timer_set(*io_timer,IDLE_PERIOD_TIME_VALUE); // функция реализации ПИ регулятора idl_prstate.output_state = (error * factor) / 4 + (iState_error / 40)*d->param.ifac2; //idl_prstate.output_state = (idl_prstate.output_state * ((d->param.idling_rpm + idl_coolant_rpm.output_state)/d->sens.frequen)* factor) / 4; //user_var3 = d->param.ifac1; } //ограничиваем коррекцию нижним и верхним пределами регулирования restrict_value_to(&idl_prstate.output_state, d->param.idlreg_min_angle, d->param.idlreg_max_angle); return idl_prstate.output_state; }
/**Main function of firmware - entry point */ MAIN() { int16_t calc_adv_ang = 0; uint8_t turnout_low_priority_errors_counter = 255; int16_t advance_angle_inhibitor_state = 0; retard_state_t retard_state; //подготовка структуры данных переменных состояния системы init_ecu_data(&edat); knklogic_init(&retard_state); //конфигурируем порты ввода/вывода ckps_init_ports(); vent_init_ports(); fuelecon_init_ports(); idlecon_init_ports(); starter_init_ports(); ce_init_ports(); knock_init_ports(); jumper_init_ports(); //если код программы испорчен - зажигаем СЕ if (crc16f(0, CODE_SIZE)!=PGM_GET_WORD(&fw_data.code_crc)) ce_set_error(ECUERROR_PROGRAM_CODE_BROKEN); //читаем параметры load_eeprom_params(&edat); #ifdef REALTIME_TABLES //load currently selected tables into RAM load_selected_tables_into_ram(&edat); #endif //предварительная инициализация параметров сигнального процессора детонации knock_set_band_pass(edat.param.knock_bpf_frequency); knock_set_gain(PGM_GET_BYTE(&fw_data.exdata.attenuator_table[0])); knock_set_int_time_constant(edat.param.knock_int_time_const); if (edat.param.knock_use_knock_channel) if (!knock_module_initialize()) {//чип сигнального процессора детонации неисправен - зажигаем СЕ ce_set_error(ECUERROR_KSP_CHIP_FAILED); } edat.use_knock_channel_prev = edat.param.knock_use_knock_channel; adc_init(); //проводим несколько циклов измерения датчиков для инициализации данных meas_initial_measure(&edat); //снимаем блокировку стартера starter_set_blocking_state(0); //инициализируем UART uart_init(edat.param.uart_divisor); //инициализируем модуль ДПКВ ckps_init_state(); ckps_set_cyl_number(edat.param.ckps_engine_cyl); ckps_set_edge_type(edat.param.ckps_edge_type); ckps_set_cogs_btdc(edat.param.ckps_cogs_btdc); //<--only partial initialization #ifndef COIL_REGULATION ckps_set_ignition_cogs(edat.param.ckps_ignit_cogs); #endif ckps_set_knock_window(edat.param.knock_k_wnd_begin_angle,edat.param.knock_k_wnd_end_angle); ckps_use_knock_channel(edat.param.knock_use_knock_channel); ckps_set_cogs_btdc(edat.param.ckps_cogs_btdc); //<--now valid initialization ckps_set_merge_outs(edat.param.merge_ign_outs); s_timer_init(); vent_init_state(); //разрешаем глобально прерывания _ENABLE_INTERRUPT(); sop_init_operations(); //------------------------------------------------------------------------ while(1) { if (ckps_is_cog_changed()) s_timer_set(engine_rotation_timeout_counter,ENGINE_ROTATION_TIMEOUT_VALUE); if (s_timer_is_action(engine_rotation_timeout_counter)) { //двигатель остановился (его обороты ниже критических) #ifdef COIL_REGULATION ckps_init_ports(); //чтобы IGBT не зависли в открытом состоянии //TODO: Сделать мягкую отсечку для избавления от нежелательной искры. Как? #endif ckps_init_state_variables(); edat.engine_mode = EM_START; //режим пуска if (edat.param.knock_use_knock_channel) knock_start_settings_latching(); edat.curr_angle = calc_adv_ang; meas_update_values_buffers(&edat, 1); //<-- update RPM only } //запускаем измерения АЦП, через равные промежутки времени. При обнаружении каждого рабочего //цикла этот таймер переинициализируется. Таким образом, когда частота вращения двигателя превысит //определенную величину, это условие выполнятся перестанет. if (s_timer_is_action(force_measure_timeout_counter)) { if (!edat.param.knock_use_knock_channel) { _DISABLE_INTERRUPT(); adc_begin_measure(); _ENABLE_INTERRUPT(); } else { //если сейчас происходит загрузка настроек в HIP, то нужно дождаться ее завершения. while(!knock_is_latching_idle()); _DISABLE_INTERRUPT(); //включаем режим интегрирования и ждем около 20мкс, пока интегратор начнет интегрировать (напряжение //на его выходе упадет до минимума). В данном случае нет ничего страшного в том, что мы держим прерывания //запрещенными 20-25мкс, так как это проискодит на очень маленьких оборотах. knock_set_integration_mode(KNOCK_INTMODE_INT); _DELAY_CYCLES(350); knock_set_integration_mode(KNOCK_INTMODE_HOLD); adc_begin_measure_all(); //измеряем сигнал с ДД тоже _ENABLE_INTERRUPT(); } s_timer_set(force_measure_timeout_counter, FORCE_MEASURE_TIMEOUT_VALUE); meas_update_values_buffers(&edat, 0); } //----------непрерывное выполнение----------------------------------------- //выполнение отложенных операций sop_execute_operations(&edat); //управление фиксированием и индицированием возникающих ошибок ce_check_engine(&edat, &ce_control_time_counter); //обработка приходящих/уходящих данных последовательного порта process_uart_interface(&edat); //управление сохранением настроек save_param_if_need(&edat); //расчет мгновенной частоты вращения коленвала edat.sens.inst_frq = ckps_calculate_instant_freq(); //усреднение физических величин хранящихся в кольцевых буферах meas_average_measured_values(&edat); //cчитываем дискретные входы системы и переключаем тип топлива meas_take_discrete_inputs(&edat); //управление периферией control_engine_units(&edat); //КА состояний системы (диспетчер режимов - сердце основного цикла) calc_adv_ang = advance_angle_state_machine(&edat); //добавляем к УОЗ октан-коррекцию calc_adv_ang+=edat.param.angle_corr; //ограничиваем получившийся УОЗ установленными пределами restrict_value_to(&calc_adv_ang, edat.param.min_angle, edat.param.max_angle); //Если стоит режим нулевого УОЗ, то 0 if (edat.param.zero_adv_ang) calc_adv_ang = 0; #ifdef COIL_REGULATION //calculate and update coil regulation time ckps_set_acc_time(accumulation_time(&edat)); #endif //Если разрешено, то делаем отсечку зажигания при превышении определенных оборотов if (edat.param.ign_cutoff) ckps_enable_ignition(edat.sens.inst_frq < edat.param.ign_cutoff_thrd); else ckps_enable_ignition(1); //------------------------------------------------------------------------ //выполняем операции которые необходимо выполнять строго для каждого рабочего цикла. if (ckps_is_cycle_cutover_r()) { meas_update_values_buffers(&edat, 0); s_timer_set(force_measure_timeout_counter, FORCE_MEASURE_TIMEOUT_VALUE); //Ограничиваем быстрые изменения УОЗ, он не может изменится больше чем на определенную величину //за один рабочий цикл. В режиме пуска фильтр УОЗ отключен. if (EM_START == edat.engine_mode) edat.curr_angle = advance_angle_inhibitor_state = calc_adv_ang; else edat.curr_angle = advance_angle_inhibitor(calc_adv_ang, &advance_angle_inhibitor_state, edat.param.angle_inc_spead, edat.param.angle_dec_spead); //---------------------------------------------- if (edat.param.knock_use_knock_channel) { knklogic_detect(&edat, &retard_state); knklogic_retard(&edat, &retard_state); } else edat.knock_retard = 0; //---------------------------------------------- //сохраняем УОЗ для реализации в ближайшем по времени цикле зажигания ckps_set_advance_angle(edat.curr_angle); //управляем усилением аттенюатора в зависимости от оборотов if (edat.param.knock_use_knock_channel) knock_set_gain(knock_attenuator_function(&edat)); // индицирование этих ошибок прекращаем при начале вращения двигателя //(при прошествии N-го количества циклов) if (turnout_low_priority_errors_counter == 1) { ce_clear_error(ECUERROR_EEPROM_PARAM_BROKEN); ce_clear_error(ECUERROR_PROGRAM_CODE_BROKEN); } if (turnout_low_priority_errors_counter > 0) turnout_low_priority_errors_counter--; } }//main loop //------------------------------------------------------------------------ }