/** Special function for processing falling edge, * must be called from ISR * \param tmr Timer value at the moment of falling edge */ INLINE void ProcessFallingEdge(uint16_t tmr) { //save period value if it is correct. We need to do it forst of all to have fresh stroke_period value if (CHECKBIT(flags, F_VHTPER)) { //calculate stroke period hall.stroke_period = tmr - hall.measure_start_value; WRITEBIT(flags, F_SPSIGN, tmr < hall.measure_start_value); //save sign hall.t1oc_s = hall.t1oc, hall.t1oc = 0; //save value and reset counter } SETBIT(flags, F_VHTPER); SETBIT(flags, F_STROKE); //set the stroke-synchronization event (устанавливаем событие тактовой синхронизации) hall.measure_start_value = tmr; if (!CHECKBIT(flags2, F_SHUTTER_S)) { uint16_t delay; #ifdef STROBOSCOPE hall.strobe = 1; //strobe! #endif //----------------------------------------------------- //Software PWM is very sensitive even to small delays. So, we need to allow OCF2 and TOV2 //interrupts occur during processing of this handler. #ifdef COOLINGFAN_PWM _ENABLE_INTERRUPT(); #endif //----------------------------------------------------- //start timer for counting out of advance angle (spark) delay = (((uint32_t)hall.advance_angle * hall.stroke_period) / hall.degrees_per_stroke); #ifdef COOLINGFAN_PWM _DISABLE_INTERRUPT(); #endif OCR1A = tmr + ((delay < 15) ? 15 : delay) - CALIBRATION_DELAY; //set compare channel, additionally prevent spark missing when advance angle is near to 60° TIFR1 = _BV(OCF1A); TIMSK1|= _BV(OCIE1A); //start timer for countiong out of knock window opening if (CHECKBIT(flags, F_USEKNK)) { #ifdef COOLINGFAN_PWM _ENABLE_INTERRUPT(); #endif delay = ((uint32_t)hall.knock_wnd_begin * hall.stroke_period) / hall.degrees_per_stroke; #ifdef COOLINGFAN_PWM _DISABLE_INTERRUPT(); #endif set_timer0(delay); hall.knkwnd_mode = 0; } knock_start_settings_latching();//start the process of downloading the settings into the HIP9011 (запускаем процесс загрузки настроек в HIP) adc_begin_measure(_AB(hall.stroke_period, 1) < 4);//start the process of measuring analog input values (запуск процесса измерения значений аналоговых входов) } }
void gdstpmot_run(uint16_t steps) { if (steps) { _DISABLE_INTERRUPT(); gdsm_steps_cnt = 0; _ENABLE_INTERRUPT(); } _DISABLE_INTERRUPT(); gdsm_steps = steps; gdsm_latch = 1; _ENABLE_INTERRUPT(); }
void stpmot_run(uint16_t steps) { _DISABLE_INTERRUPT(); sm_steps = steps; sm_latch = 1; _ENABLE_INTERRUPT(); }
uint16_t gdstpmot_stpcnt(void) { uint16_t count; _DISABLE_INTERRUPT(); count = gdsm_steps_cnt; _ENABLE_INTERRUPT(); return count; }
uint8_t eeprom_take_completed_opcode(void) { uint8_t result; _DISABLE_INTERRUPT(); result = eewd.completed_opcode; eewd.completed_opcode = 0; _ENABLE_INTERRUPT(); return result; }
uint8_t stpmot_is_busy(void) { uint16_t current; uint8_t latching; _DISABLE_INTERRUPT(); current = sm_steps_b; latching = sm_latch; _ENABLE_INTERRUPT(); return (current > 0 || latching); //busy? }
void ckps_set_cyl_number(uint8_t i_cyl_number) { uint16_t degrees_per_stroke; uint8_t _t; _t = _SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); hall.chan_number = i_cyl_number; //set new value _RESTORE_INTERRUPT(_t); hall.frq_calc_dividend = FRQ_CALC_DIVIDEND(i_cyl_number); //precalculate value of degrees per 1 engine stroke (value * ANGLE_MULTIPLIER) degrees_per_stroke = (720 * ANGLE_MULTIPLIER) / i_cyl_number; _t = _SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); hall.io_callback = IOCFG_CB(IOP_IGN_OUT1); //use single output hall.degrees_per_stroke = degrees_per_stroke; _RESTORE_INTERRUPT(_t); }
void bc_indication_mode(struct ecudata_t *d) { uint8_t i = 5; if (!IOCFG_CHECK(IOP_BC_INPUT)) return; //normal program execution //Check 5 times do { if (IOCFG_GET(IOP_BC_INPUT)) return; //normal program execution }while(--i); //We are entered to the blink codes indication mode _DISABLE_INTERRUPT(); ce_set_state(CE_STATE_OFF); vent_turnoff(d); //turn off ventilator starter_set_blocking_state(1); //block starter IOCFG_INIT(IOP_FL_PUMP, 0); //turn off fuel pump IOCFG_INIT(IOP_IE, 0); //turn off IE valve solenoid IOCFG_INIT(IOP_FE, 0); //turn off power valve solenoid wdt_reset_timer(); //delay 2 sec. delay_hom(20); //main loop for(;;) { uint16_t errors = 0; disp_start(); delay_hom(7); //read errors eeprom_read(&errors, EEPROM_ECUERRORS_START, sizeof(uint16_t)); for(i = 0; i < 16; ++i) { if (0 == PGM_GET_BYTE(&blink_codes[i])) continue; if (errors & (1 << i)) { disp_code(PGM_GET_BYTE(&blink_codes[i])); delay_hom(20); } } delay_hom(20); wdt_reset_timer(); } }
//Instantaneous frequency calculation of crankshaft rotation from the measured period between the engine strokes //(for example for 4-cylinder, 4-stroke it is 180°) //Period measured in the discretes of timer (one discrete = 4us), one minute = 60 seconds, one second has 1,000,000 us. //Высчитывание мгновенной частоты вращения коленвала по измеренному периоду между тактами двигателя //(например для 4-цилиндрового, 4-х тактного это 180 градусов) //Период в дискретах таймера (одна дискрета = 4мкс), в одной минуте 60 сек, в одной секунде 1000000 мкс. uint16_t ckps_calculate_instant_freq(void) { uint16_t period; uint8_t ovfcnt, sign; //ensure atomic acces to variable (обеспечиваем атомарный доступ к переменной) _DISABLE_INTERRUPT(); period = hall.stroke_period; //stroke period ovfcnt = hall.t1oc_s; //number of timer overflows sign = CHECKBIT(flags, F_SPSIGN); //sign of stroke period _ENABLE_INTERRUPT(); //We know period and number of timer overflows, so we can calculate correct value of RPM even if RPM is very low if (sign && ovfcnt > 0) return hall.frq_calc_dividend / ((((int32_t)ovfcnt) * 65536) - (65536-period)); else return hall.frq_calc_dividend / ((((int32_t)ovfcnt) * 65536) + period); }
void eeprom_read(void* sram_dest, uint16_t eeaddr, uint16_t size) { uint8_t _t; uint8_t *dest = (uint8_t*)sram_dest; do { _t=_SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); __EEGET(*dest,eeaddr); _RESTORE_INTERRUPT(_t); eeaddr++; dest++; }while(--size); EEAR=0x000; //this will help to prevent corruption of EEPROM }
uint8_t knock_module_initialize(void) { uint8_t i, response; uint8_t init_data[2] = {KSP_SET_PRESCALER | KSP_PRESCALER_VALUE | KSP_SO_TERMINAL_ACTIVE, KSP_SET_CHANNEL | KSP_CHANNEL_0}; uint8_t _t; _t=_SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); //Setting HOLD mode for integrator and "Run" mode for chip at all. SET_KSP_TEST(1); SET_KSP_INTHOLD(KNOCK_INTMODE_HOLD); SET_KSP_CS(1); spi_master_init(); ksp.ksp_interrupt_state = 0; //init state machine ksp.ksp_error = 0; //set prescaler first SET_KSP_CS(0); spi_master_transmit(init_data[0]); SET_KSP_CS(1); //Setting SO terminal active and perform initialization. For each parameter perform //checking for response and correcntess of received data. for(i = 0; i < 2; ++i) { SET_KSP_CS(0); spi_master_transmit(init_data[i]); SET_KSP_CS(1); response = SPDR; if (response!=init_data[i]) { _RESTORE_INTERRUPT(_t); return 0; //error - chip doesn't respond! } } _RESTORE_INTERRUPT(_t); //Initialization completed successfully return 1; }
void eeprom_write(const void* sram_src, uint16_t eeaddr, uint16_t size) { uint8_t _t; uint8_t *src = (uint8_t*)sram_src; do { _t=_SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); __EEPUT(eeaddr, *src); _RESTORE_INTERRUPT(_t); wdt_reset_timer(); eeaddr++; src++; }while(--size); EEAR=0x000; //this will help to prevent corruption of EEPROM }
void eeprom_write_P(void _PGM *pgm_src, uint16_t eeaddr, uint16_t size) { uint8_t _t; uint8_t _PGM *src = (uint8_t _PGM*)pgm_src; do { uint8_t byte = PGM_GET_BYTE(src); _t=_SAVE_INTERRUPT(); _DISABLE_INTERRUPT(); __EEPUT(eeaddr, byte); _RESTORE_INTERRUPT(_t); wdt_reset_timer(); eeaddr++; src++; }while(--size); EEAR=0x000; //this will help to prevent corruption of EEPROM }
/**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 //------------------------------------------------------------------------ }