//If any error occurs, the CE is light up for a fixed time. If the problem persists (eg corrupted the program code), //then the CE will be turned on continuously. At the start of program CE lights up for 0.5 seconds. for indicating //of the operability. void ce_check_engine(struct ecudata_t* d, volatile s_timer8_t* ce_control_time_counter) { uint16_t temp_errors; check(d); //If the timer counted the time, then turn off the CE if (s_timer_is_action(*ce_control_time_counter)) { ce_set_state(CE_STATE_OFF); d->ce_state = 0; //<--doubling } //If at least one error is present - turn on CE and start timer if (ce_state.ecuerrors!=0) { s_timer_set(*ce_control_time_counter, CE_CONTROL_STATE_TIME_VALUE); ce_set_state(CE_STATE_ON); d->ce_state = 1; //<--doubling } temp_errors = (ce_state.merged_errors | ce_state.ecuerrors); //check for error which is still not in merged_errors if (temp_errors!=ce_state.merged_errors) { //Because at the time of appearing of a new error, EEPROM can be busy (for example, saving options), //then it is necessary to run deffered operation, which will be automatically executed as soon as the EEPROM //will be released. sop_set_operation(SOP_SAVE_CE_MERGED_ERRORS); } ce_state.merged_errors = temp_errors; //copy error's bits into the cache for transferring d->ecuerrors_for_transfer|= ce_state.ecuerrors; }
void process_uart_interface(struct ecudata_t* d) { uint8_t descriptor; //Following code executes at start up only if bluetooth is enabled and only 1 time if (d->param.bt_flags & _BV(BTF_USE_BT)) { if (!bt_set_baud(d, d->param.uart_divisor)) return; if (d->bt_name[0] && d->bt_pass[0]) if (!bt_set_namepass(d)) return; } if (uart_is_packet_received())//приняли новый фрейм ? { descriptor = uart_recept_packet(d); switch(descriptor) { case TEMPER_PAR: case CARBUR_PAR: case IDLREG_PAR: case ANGLES_PAR: case STARTR_PAR: case ADCCOR_PAR: case CHOKE_PAR: //если были изменены параметры то сбрасываем счетчик времени s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); break; case MISCEL_PAR: #ifdef HALL_OUTPUT ckps_set_hall_pulse(d->param.hop_start_cogs, d->param.hop_durat_cogs); #endif s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); break; case FUNSET_PAR: #ifdef REALTIME_TABLES sop_set_operation(SOP_SELECT_TABLSET); #endif //если были изменены параметры то сбрасываем счетчик времени s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); break; case OP_COMP_NC: if (_AB(d->op_actn_code, 0) == OPCODE_EEPROM_PARAM_SAVE) //приняли команду сохранения параметров { sop_set_operation(SOP_SAVE_PARAMETERS); _AB(d->op_actn_code, 0) = 0; //обработали } if (_AB(d->op_actn_code, 0) == OPCODE_CE_SAVE_ERRORS) //приняли команду чтения сохраненных кодов ошибок { sop_set_operation(SOP_READ_CE_ERRORS); _AB(d->op_actn_code, 0) = 0; //обработали } if (_AB(d->op_actn_code, 0) == OPCODE_READ_FW_SIG_INFO) //приняли команду чтения и передачи информации о прошивке { sop_set_operation(SOP_SEND_FW_SIG_INFO); _AB(d->op_actn_code, 0) = 0; //обработали } #ifdef REALTIME_TABLES if (_AB(d->op_actn_code, 0) == OPCODE_LOAD_TABLSET) //приняли команду выбора нового набора таблиц { sop_set_operation(SOP_LOAD_TABLSET); _AB(d->op_actn_code, 0) = 0; //обработали } if (_AB(d->op_actn_code, 0) == OPCODE_SAVE_TABLSET) //приняли команду сохранения набора таблиц для указанного типа топлива { sop_set_operation(SOP_SAVE_TABLSET); _AB(d->op_actn_code, 0) = 0; //обработали } #endif #ifdef DIAGNOSTICS if (_AB(d->op_actn_code, 0) == OPCODE_DIAGNOST_ENTER) //"enter diagnostic mode" command has been received { //this function will send confirmation answer and start diagnostic mode (it will has its own separate loop) diagnost_start(); _AB(d->op_actn_code, 0) = 0; //обработали } if (_AB(d->op_actn_code, 0) == OPCODE_DIAGNOST_LEAVE) //"leave diagnostic mode" command has been received { //this function will send confirmation answer and reset device diagnost_stop(); _AB(d->op_actn_code, 0) = 0; //обработали } #endif if (_AB(d->op_actn_code, 0) == OPCODE_RESET_EEPROM) //reset EEPROM command received { if (_AB(d->op_actn_code, 1) == 0xAA) //second byte must be 0xAA sop_set_operation(SOP_SEND_NC_RESET_EEPROM); _AB(d->op_actn_code, 0) = 0; //processed } break; case CE_SAVED_ERR: sop_set_operation(SOP_SAVE_CE_ERRORS); break; case CKPS_PAR: //если были изменены параметры ДПКВ, то немедленно применяем их на работающем двигателе и сбрасываем счетчик времени ckps_set_cyl_number(d->param.ckps_engine_cyl); //<--обязательно в первую очередь! ckps_set_cogs_num(d->param.ckps_cogs_num, d->param.ckps_miss_num); ckps_set_edge_type(d->param.ckps_edge_type); #ifdef SECU3T cams_vr_set_edge_type(d->param.ref_s_edge_type); //REF_S (ДНО) #endif ckps_set_cogs_btdc(d->param.ckps_cogs_btdc); ckps_set_merge_outs(d->param.merge_ign_outs); #ifndef DWELL_CONTROL ckps_set_ignition_cogs(d->param.ckps_ignit_cogs); #endif s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); #ifdef HALL_SYNC ckps_select_input(d->param.hall_flags & _BV(HSF_USECKPINP)); //select input (CKPS or PS) #endif break; case KNOCK_PAR: //аналогично для контороля детонации, обязательно после CKPS_PAR! //инициализируем процессор детонации в случае если он не использовался, а теперь поступила команда его использовать. if (!d->use_knock_channel_prev && d->param.knock_use_knock_channel) if (!knock_module_initialize()) {//чип сигнального процессора детонации неисправен - зажигаем СЕ ce_set_error(ECUERROR_KSP_CHIP_FAILED); } knock_set_band_pass(d->param.knock_bpf_frequency); //gain устанавливается в каждом рабочем цикле knock_set_int_time_constant(d->param.knock_int_time_const); ckps_set_knock_window(d->param.knock_k_wnd_begin_angle, d->param.knock_k_wnd_end_angle); ckps_use_knock_channel(d->param.knock_use_knock_channel); //запоминаем состояние флага для того чтобы потом можно было определить нужно инициализировать //процессор детонации или нет. d->use_knock_channel_prev = d->param.knock_use_knock_channel; //если были изменены параметры то сбрасываем счетчик времени s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); break; case SECUR_PAR: if (d->bt_name[0] && d->bt_pass[0]) bt_start_set_namepass(); s_timer16_set(save_param_timeout_counter, SAVE_PARAM_TIMEOUT_VALUE); break; } //мы обработали принятые данные - приемник ничем теперь не озабочен uart_notify_processed(); } //периодически передаем фреймы с данными if (s_timer_is_action(send_packet_interval_counter)) { if (!uart_is_sender_busy()) { uint8_t desc = uart_get_send_mode(); uart_send_packet(d, 0); //теперь передатчик озабочен передачей данных #ifdef DEBUG_VARIABLES if (SENSOR_DAT==desc || ADCRAW_DAT==desc || CE_ERR_CODES==desc || DIAGINP_DAT==desc) sop_set_operation(SOP_DBGVAR_SENDING); //additionally we will send packet with debug information #endif s_timer_set(send_packet_interval_counter, d->param.uart_period_t_ms); //после передачи очищаем кеш ошибок, передача битов ошибок осуществляется только в 1 из 2 пакетов if (SENSOR_DAT==desc || CE_ERR_CODES==desc) d->ecuerrors_for_transfer = 0; } } }
/**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 //------------------------------------------------------------------------ }