uint16_t accumulation_time(struct ecudata_t* d) { int16_t i, i1, voltage = d->sens.voltage; if (voltage < VOLTAGE_MAGNITUDE(5.4)) voltage = VOLTAGE_MAGNITUDE(5.4); //5.4 - минимальное значение напряжения в таблице предусмотренной для 12В бортовой сети i = (voltage - VOLTAGE_MAGNITUDE(5.4)) / VOLTAGE_MAGNITUDE(0.4); //0.4 - шаг по напряжению if (i >= COIL_ON_TIME_LOOKUP_TABLE_SIZE-1) i = i1 = COIL_ON_TIME_LOOKUP_TABLE_SIZE-1; else i1 = i + 1; return simple_interpolation(voltage, PGM_GET_WORD(&fw_data.exdata.coil_on_time[i]), PGM_GET_WORD(&fw_data.exdata.coil_on_time[i1]), (i * VOLTAGE_MAGNITUDE(0.4)) + VOLTAGE_MAGNITUDE(5.4), VOLTAGE_MAGNITUDE(0.4)) >> 4; }
// Реализует функцию УОЗ от оборотов для холостого хода // Возвращает значение угла опережения в целом виде * 32. 2 * 16 = 32. int16_t idling_function(struct ecudata_t* d) { int8_t i; int16_t rpm = d->sens.inst_frq; //находим узлы интерполяции, вводим ограничение если обороты выходят за пределы for(i = 14; i >= 0; i--) if (d->sens.inst_frq >= PGM_GET_WORD(&f_slots_ranges[i])) break; if (i < 0) {i = 0; rpm = 600;} return simple_interpolation(rpm, _GB(&d->fn_dat->f_idl[i]), _GB(&d->fn_dat->f_idl[i+1]), PGM_GET_WORD(&f_slots_ranges[i]), PGM_GET_WORD(&f_slots_length[i])); }
// Реализует функцию УОЗ от оборотов(мин-1) и нагрузки(кПа) для рабочего режима двигателя // Возвращает значение угла опережения в целом виде * 32, 2 * 16 = 32. int16_t work_function(struct ecudata_t* d, uint8_t i_update_airflow_only) { int16_t gradient, discharge, rpm = d->sens.inst_frq, l; int8_t f, fp1, lp1; discharge = (d->param.map_upper_pressure - d->sens.map); if (discharge < 0) discharge = 0; //map_upper_pressure - верхнее значение давления //map_lower_pressure - нижнее значение давления gradient = (d->param.map_upper_pressure - d->param.map_lower_pressure) / 16; //делим на количество узлов интерполяции по оси давления if (gradient < 1) gradient = 1; //исключаем деление на ноль и отрицательное значение если верхнее давление меньше нижнего l = (discharge / gradient); if (l >= (F_WRK_POINTS_F - 1)) lp1 = l = F_WRK_POINTS_F - 1; else lp1 = l + 1; //обновляем переменную расхода воздуха d->airflow = 16 - l; if (i_update_airflow_only) return 0; //выходим если вызвавший указал что мы должны обновить только расход воздуха //находим узлы интерполяции, вводим ограничение если обороты выходят за пределы for(f = 14; f >= 0; f--) if (rpm >= PGM_GET_WORD(&f_slots_ranges[f])) break; //рабочая карта работает на 600-х оборотах и выше if (f < 0) {f = 0; rpm = 600;} fp1 = f + 1; return bilinear_interpolation(rpm, discharge, _GB(&d->fn_dat->f_wrk[l][f]), _GB(&d->fn_dat->f_wrk[lp1][f]), _GB(&d->fn_dat->f_wrk[lp1][fp1]), _GB(&d->fn_dat->f_wrk[l][fp1]), PGM_GET_WORD(&f_slots_ranges[f]), (gradient * l), PGM_GET_WORD(&f_slots_length[f]), gradient); }
//Coolant sensor is thermistor (тип датчика температуры - термистор) //Note: We assume that voltage on the input of ADC depend on thermistor's resistance linearly. //Voltage on the input of ADC can be calculated as following: // U3=U1*Rt*R2/(Rp(Rt+R1+R2)+Rt(R1+R2)); // Rt - thermistor, Rp - pulls up thermistor to voltage U1, // R1,R2 - voltage divider resistors. int16_t thermistor_lookup(uint16_t adcvalue) { int16_t i, i1; //Voltage value at the start of axis in ADC discretes (значение напряжения в начале оси в дискретах АЦП) uint16_t v_start = PGM_GET_WORD(&fw_data.exdata.cts_vl_begin); //Voltage value at the end of axis in ADC discretes (значение напряжения в конце оси в дискретах АЦП) uint16_t v_end = PGM_GET_WORD(&fw_data.exdata.cts_vl_end); uint16_t v_step = (v_end - v_start) / (THERMISTOR_LOOKUP_TABLE_SIZE - 1); if (adcvalue < v_start) adcvalue = v_start; i = (adcvalue - v_start) / v_step; if (i >= THERMISTOR_LOOKUP_TABLE_SIZE-1) i = i1 = THERMISTOR_LOOKUP_TABLE_SIZE-1; else i1 = i + 1; return (simple_interpolation(adcvalue, PGM_GET_WORD(&fw_data.exdata.cts_curve[i]), PGM_GET_WORD(&fw_data.exdata.cts_curve[i1]), (i * v_step) + v_start, v_step)) >> 4; }
/**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 //------------------------------------------------------------------------ }