Программный модуль для чайника RK-M171S. Версия 2.6



ПО является неотъемлемой частью чайника RK-M171S, отдельно потребителю не поставляется и эксплуатируется только в составе устройства.

Фрагмент исходного кода
////////////////////////////////////////////////////////////////

//RK-M171S

//v2.6

//application.c

////////////////////////////////////////////////////////////////

 
/********* HEADER FILE INCLUDES *************************************************/
#include "math.h"
#include "keyb.h"
#include "timers.h"
#include "kpt_2383ab.h"
#include "seg7_dynamic_ind.h"
#include "temp.h"
#include "err.h"
#include "buz.h"
#include "config.h"
#include "heat.h"
#include "err.h"
#include "boost_ctrl.h"
#include "app_ind.h"
#include "test.h"
#include "application.h"
/********* DEFINES **************************************************************/
/********* MACROS ***************************************************************/
/********* GLOBAL VARIABLES *****************************************************/
/********* LOCAL VARIABLES ******************************************************/
static app_state_t 	app_state  = APP_START;	//APP_STAND_BY;//									/** Состояние главной state machine*/
static BOOL app_simple_timeout_flag = 0;														
static BOOL app_param_adj_timer_flag = 0;														/** флаг по которому при удеражнии кнопки изменяется задаваемый параметер*/
static unsigned char setting_keepwarm_temp 	= APP_KEEPWARM_DEFAULT_TEMP_C;						/** темпереатура поддержания(подогрева) задается в диапазоне  */
static unsigned char stand_by_ind_temp 	= APP_KEEPWARM_DEFAULT_TEMP_C;							/** темпереhатура отображения в режиме stand by */
static app_sound_mode_t	sound_mode = APP_SOUND_ON;												/** Состояние звуковых сигналов(вкл/выкл)*/
static error_type_t app_error = ERR_NO;														
static app_unpairing_flag_t unpairing_flag 				= APP_UNPARING_KEEP_PAIR;				/** Флаг команды unpairing-а*/
static bool unpairing_event_trigger 					= false;								/** Флаг используется для отслеживания возникновения события unpairing-а*/
static app_pairing_clear_flag_t pairing_flag 			= APP_MAINTAIN_PARING;					/** Флаг режима pairing-а*/
static keep_warm_ctrl_type_t	keep_warm_ctrl_type		= KEEP_WARM_CTRL_REG;					/** алгоритм поддержания температуры регулятор или разгон */
static boost_state_type_t		boost_state				= BOOST_FINISHED;						/** состояние разгона */
static unsigned int keep_warm_time_downcounter_s = APP_KEEPWARM_MAX_TIME_S;	 					/** счетчик времени подогрева */
static unsigned int boiling_min_time_downcounter_s = APP_BOILING_MIN_TIME_S;					/** счетчик минимального времени кипения */
static unsigned int block_ind_time_downcounter_s = 0;											/** счетчик времени индикации блокировки клавитуры */
static unsigned int stand_by_start_ind_time_downcounter_s = APP_STAND_BY_START_IND_TIME_S;		/** счетчик времени индикации стартового состояния */
static unsigned int keep_warm_temp_set_time_downcounter_s = 0;		/** счетчик времени мигания температуры подогрева при ее изменении */
static unsigned int boinig_complete_ind_time_downcounter_s = APP_BOILING_COMPLETE_IND_TIME_S;	/** счетчик времени индикации стартового состояния */
static bool boiling_complete_flag = FALSE;														/** Флаг завершения процесса кипечения */
static bool boiling_complete_flag_prew = FALSE;													/** Флаг завершения процесса кипечения предъидущее значение*/
static bool keep_warm_boost_complete_flag = FALSE;												/** Флаг завершения процесса выхода на температуру поддержания */
static bool keep_warm_boost_complete_flag_prew = FALSE;											/** Флаг завершения процесса выхода на температуру поддержания предъидущее значение*/
static bool keep_warm_enable = FALSE;															/** Флаг разрешения подогрева */
static app_stand_by_temp_ind_t stand_by_temp_ind_state = STAND_BY_TEMP_IND_NONE;				/** Состояние отображения температуры в режиме stand_by*/
/********* FUNCTION PROTOTYPES **************************************************/
static void app_simple_timeout_timer_handler(void);
/********* FUNCTIONS IMPLEMENTATION *********************************************/
/**
 *  @brief  Индикация режима APP_START  
 *  
 *  @param  none     
 *  @return none
 *			
 */
static void appl_show_start(void){
	app_ind_show_set_temp(188, false);
	return;
}
/**
 *  @brief  Индикация режима APP_STAND_BY  
 *  
 *  @param[in]  temp_ind_en	- TRUE - индикация температуры разрешена, FALSE - запрещена
 *	@param[in]	temp		- температура для индикации
 *  @return none
 *			
 */
static void appl_show_stand_by( bool temp_ind_en, uint8_t temp ){
	if( temp_ind_en == false )
	{
		app_ind_hide_set_temp();
		kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 0 );
	}else{
		if( temp > 100 )temp = 100;
		app_ind_show_set_temp(temp, false);
		kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 1 );
	}	
	return;
}
/**
 *  @brief  Индикация режима APP_EMPTY_KETTLE  
 *  
 *  @param  none
 *  @return none
 *			
 */
/** Функция не используется т.к. невозможно определить пустой чайник*/


/**
 *  @brief  Индикация режима APP_PAIRING
 *  
 *  @param  none
 *  @return none
 *			
 */
static void appl_show_pairing(void){
	kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 	0 );
	kpt_2383ab_cmd( KPT_2383AB_CMD_BACKLIGHT_CTRL, 1 );	
	app_ind_show_pairing();		
	return;
}
/**
 *  @brief  Индикация режима APP_BOILING_PROC
 *  
 *  @param[in]  keep_warm_en	- подогрев разрешен
 * 	@param[in]	temp			- температура уставки для индикации
 * 	@param[in]	temp_blink		- температура мигает или нет
 *  @return none
 *			
 */
static void app_show_boiling(bool keep_warm_en, unsigned char temp, bool temp_blink){
	
	kpt_2383ab_cmd( KPT_2383AB_CMD_BACKLIGHT_CTRL, 1 );				
	app_ind_show_set_temp(temp, temp_blink);
	if( true == keep_warm_en ){
		kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 	1 );
	}else{
		kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 	0 );
	}
	//app_ind_show_temp(temp_get_val());	
	return;
}
/**
 *  @brief  Индикация режима APP_KEEPWARM_PROC
 *  
 *  @param[in]  temp			- температура уставки для индикации
 * 	@param[in]	temp_blink		- температура мигает или нет
 * 	@param[in]	keep_warm_boost_complete - разгон поддержания закончен или нет
 *  @return none
 *			
 */
static void app_show_keepwarm(unsigned char temp, bool temp_blink, bool keep_warm_boost_complete){
	kpt_2383ab_cmd( KPT_2383AB_CMD_BACKLIGHT_CTRL, 1 );			
	app_ind_show_set_temp(temp, temp_blink);
	kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 	1 );
	//app_ind_show_temp(temp_get_val());
	return;
}
/**
 *  @brief  Индикация режима APP_ERROR
 *  
 *  @param[in]  err			- ошибка для индикации
 *  @return none
 *			
 */
static void app_show_error(error_type_t err){
	kpt_2383ab_cmd( KPT_2383AB_CMD_SET_ICON_DATA, KPT_2383AB_DOT, 	0 );
	kpt_2383ab_cmd( KPT_2383AB_CMD_BACKLIGHT_CTRL, 1 );
	app_ind_show_err(err);		
}
/**
 *  @brief  Обработчик таймера отмеряющего таймаут приложения. Используется для переходов state mashine
 *  
 *  @param  none
 *  @return none
 *			
 */
static void app_simple_timeout_timer_handler(void){
	app_simple_timeout_flag = 1;
	return;
}
/**
 *  @brief  Обработчик таймера отмеряющего таймаут изменения параметров
 *  
 *  @param  none
 *  @return none
 *			
 */
static void app_param_adj_timer_handler(void){
	app_param_adj_timer_flag = 1;
	return;
}
static bool app_param_adj_timer_expired(const key_data_t* key_data){
	if(key_data->key_trigger == KEY_SHORT_PRES){
			timer_set( TIMER_APP_PARAM_ADJ, TIMER_MILISECOND(APP_PARAM_ADJ_SPEED_SLOW_MS) );	
			timer_stop( TIMER_APP_PARAM_ADJ );
			app_param_adj_timer_flag = 0;		
			timer_reset(TIMER_APP_PARAM_ADJ);
			return TRUE;
	}			

	return FALSE;
}
/**
 *  @brief  Обработчик секундного таймера приложения. 
 * 			Импользуется для: 	1. изменения индикации внутри одного сотояния state mashine
 *   							2. отсчета времени поддержания
 * 								3. отсчета минимального времени кипечения
 *  @param  none
 *  @return none
 *			
 */
static void app_sec_timer_handler(void){
	if( TRUE == keep_warm_boost_complete_flag ){
		if( 0 != keep_warm_time_downcounter_s )keep_warm_time_downcounter_s--;
	}
	if( TRUE == boiling_complete_flag ){
		if( 0 != boinig_complete_ind_time_downcounter_s )boinig_complete_ind_time_downcounter_s--;
	}	
	if( 0 != boiling_min_time_downcounter_s )boiling_min_time_downcounter_s--;
	if( 0 != block_ind_time_downcounter_s )block_ind_time_downcounter_s--;
	if( 0 != stand_by_start_ind_time_downcounter_s )stand_by_start_ind_time_downcounter_s--;
	if( 0 != keep_warm_temp_set_time_downcounter_s )keep_warm_temp_set_time_downcounter_s--;
	return;
}
/**
 *  @brief  Функция сбрасывает секундный таймер
 *
 *  @param  none
 *  @return none
 *			
 */
static void app_sec_timer_reset(void){
	timer_reset( TIMER_APP_SEC_TIMER );
}
/**
 *  @brief  Вычисляет значение температуры после вычитания из нее значения step 
 *
 *  @param[in]  temp 	- исходная температура
 *	@param[in]	min_val - минимальное значение температуры
 *	@param[in]	max_val - максимальное значение температуры
 *	@param[in]	step 	- шаг изменения температуры
 *  @return -	вычисленное значение температуры
 *			
 */
static unsigned char app_sub_temp( unsigned char temp, unsigned char min_val, unsigned char max_val, unsigned char step ){
	if( temp%step != 0 ){
		if( temp < min_val + step ){
			temp = min_val;
		}else{
			temp -= temp%step;	//округление до меньшего деленного на step без остатка
		}
	}else{
		if( temp < min_val + step )
		{
			temp = min_val;
		}else{
			temp -= step;
		}	
	}
	if( temp > max_val )temp = max_val;
	return temp;
}
/**
 *  @brief  Вычисляет значение температуры после сложения ее со значением step 
 *
 *  @param[in]  temp 	- исходная температура
 *	@param[in]	min_val - минимальное значение температуры
 *	@param[in]	max_val - максимальное значение температуры
 *	@param[in]	step 	- шаг изменения температуры
 *  @return -	вычисленное значение температуры
 *			
 */
static unsigned char app_add_temp( unsigned char temp, unsigned char min_val, unsigned char max_val, unsigned char step ){
	if( temp%step != 0 ){	
		if( temp + step > max_val ){
			temp = max_val;
		}else{
			temp += (step - temp%step);	//округление до большего деленного на step без остатка
		}		
	}else{
		if( temp + step > max_val ){
			temp = max_val;
		}else{
			temp += step;
		}
	}
	if( temp < min_val )temp = min_val;
	return temp;
}
/**
 *  @brief  Простой П-регулятор
 *
 *  @param[in]  Y - значение регулируемой величины
 *	@param[in]	U - уставка
 *	@param[in]	KV - коэффициент усиления 
 *  @return -	выход регулятора
 *			
 */
static unsigned int simple_reg( unsigned char Y, unsigned char U, unsigned char KV ){
	unsigned long int X;		//reg out
	if( Y > U ){ return 0; }
	X = ( U - Y )*KV;
	
	if( X > REG_MAX_PWM	 ){
		return REG_MAX_PWM	;
	}else{
		return X;
	}
}
/**
 *  @brief  Функция перезапускает таймер отсчитывающий таймауты переходов между состояниями state machine
 *
 *  @param[in]  timeout_s - установленный таймаут в сек
 *  @return none
 *			
 */
static void app_timeout_timers_restart(U8 timeout_s){
	app_simple_timeout_flag = 0;	
	timer_set(TIMER_APP_SIMPLE_TIMEOUT, TIMER_SECOND(timeout_s));
	timer_reset(TIMER_APP_SIMPLE_TIMEOUT);	
}
/**
*  @brief 	Определяет какой алгоритм использовать для поддержания температуры - разгон или поддаржание
*
*  @param[in]	temp_cur 		- текущее значение температуры
*  @param[in]	temp_set 		- температура для поддержания
*  @return 						- алгоритм разгон/поддержание
*
*/
static keep_warm_ctrl_type_t keep_warm_get_ctrl_type( unsigned char temp_cur, unsigned char temp_set ){
	if( temp_cur + APP_TEMP_DIFFERENCE_KEEP_WARM_BOOST_START_C >= temp_set ){
		return KEEP_WARM_CTRL_REG;
	}else{
		return KEEP_WARM_CTRL_BOOST;
	}
}
/**
*  @brief 	Функция инициализирует новой состояние state machine.
*			Инициализируеются переменные и таймеры инициализация которых не зависит от того из какого состояния был переход в текущее состояние.
*			Инициализация переменных, зависящих от предъидущего состояния, производится в самой state machine или функциях изменяемых состояние state machine.
*
*  @param[in]	state 		- новое состояние state machine
*  @return 		none
*
*/
static void app_new_state_init( app_state_t state ){
	static app_state_t state_prew = APP_STAND_BY;	
	if( state == state_prew )return;
	switch( state ){
		case APP_START:
		{
			break;
		}		
		case APP_STAND_BY:
		{
			stand_by_temp_ind_state = STAND_BY_TEMP_IND_NONE;
			block_ind_time_downcounter_s = 0;
			keep_warm_temp_set_time_downcounter_s = 0;
			stand_by_start_ind_time_downcounter_s = APP_STAND_BY_START_IND_TIME_S;
			ind_reset_dynamic_ind_timer();
			app_sec_timer_reset();
			break;
		}	
		case APP_PAIRING:
		{
			app_timeout_timers_restart(APP_PAIRING_TIMEOUT_S);
			break;
		}		
		case APP_BOILING_PROC:
		{
			ind_reset_dynamic_ind_timer();
			boiling_complete_flag = FALSE;
			boiling_complete_flag_prew = FALSE;
			keep_warm_temp_set_time_downcounter_s = 0;
			boiling_min_time_downcounter_s = APP_BOILING_MIN_TIME_S;
			boinig_complete_ind_time_downcounter_s = APP_BOILING_COMPLETE_IND_TIME_S;
			break;
		}
		case APP_KEEPWARM_PROC:			
		{
			ind_reset_dynamic_ind_timer();
			app_sec_timer_reset();										//сброс таймера для отсчета времени подогрева
			keep_warm_enable = TRUE;									//флаг разрешения побогрева
			keep_warm_boost_complete_flag = FALSE;						//флаг зашершения выхода на заданную температуру
			keep_warm_boost_complete_flag_prew = FALSE;					//предъидущее значение флага зашершения выхода на заданную температуру(используется для подачи звукового сигнала)
			keep_warm_time_downcounter_s = APP_KEEPWARM_MAX_TIME_S;		//время до окончания режима поддеражания
			if( setting_keepwarm_temp > APP_KEEPWARM_MAX_TEMP_C )
			{
				setting_keepwarm_temp = APP_KEEPWARM_MAX_TEMP_C;
			}
			if( setting_keepwarm_temp < APP_KEEPWARM_MIN_TEMP_C )
			{
				setting_keepwarm_temp = APP_KEEPWARM_MIN_TEMP_C;
			}
			keep_warm_ctrl_type = keep_warm_get_ctrl_type(  temp_get_val(), setting_keepwarm_temp );
			boost_proc_init();
			heating_force_off();
			break;
		}		
		case APP_ERROR:
		{				
			break;
		}
		case APP_TEST:
		{	
			app_timeout_timers_restart(APP_TEST_TIMEOUT_S);			
			test_init();			
			break;
		}		
		default:
		{
			break;
		}
	}
	state_prew = state;
}
/**
*  @brief 	Инициализания applicatino
*
*  @param		none
*  @return 		none
*
*/
void application_init(void){
	app_ind_init();
	timer_init(TIMER_APP_PARAM_ADJ, 0, (callback_t)app_param_adj_timer_handler );
	timer_stop(TIMER_APP_PARAM_ADJ);	
	timer_init(TIMER_APP_SIMPLE_TIMEOUT, 0, (callback_t)app_simple_timeout_timer_handler );
	timer_start(TIMER_APP_SIMPLE_TIMEOUT);
	timer_init(TIMER_APP_SEC_TIMER, TIMER_SECOND(1), (callback_t)app_sec_timer_handler );
	timer_start(TIMER_APP_SEC_TIMER);
	app_timeout_timers_restart(APP_START_TIMEOUT_S);
	return;
}
/**
*  @brief 	application state machine
*
*  @param		none
*  @return 		none
*
*/
void application_proc(void){
	static keyboard_data_t 		app_keyb_data;	
	
	keybord_data_read( &app_keyb_data );
	app_new_state_init( app_state );
	switch(app_state)
	{
		case APP_START:
		{
			//transition
			if( app_simple_timeout_flag	){
				app_state	= APP_STAND_BY;		
			}			
			if( ( app_keyb_data.keys_value.plus_key.state == KEY_CONDITION_PRESSED )
				&&( app_keyb_data.keys_value.pwr_key.state == KEY_CONDITION_PRESSED ) )
			{
				app_state	= APP_TEST;	
				break;
			}
			//indication
			appl_show_start();
			break;
		}
		case APP_STAND_BY:
		{
			//indication
			if( 0 == keep_warm_temp_set_time_downcounter_s ){
				stand_by_temp_ind_state = STAND_BY_TEMP_IND_NONE;
				appl_show_stand_by( false, 0 );
			}else{
				appl_show_stand_by( true, stand_by_ind_temp );	
			}
			//app_show_boiling(FALSE, temp_get_val(), TRUE);
			
			//work	
			heating_process_off();			
			//controll
			
			if( app_keyb_data.keys_value.minus_key.key_timer > KEY_POLLING_TIMER_S( APP_UNPAIRING_ENTER_TIME_S ) ){												
				if( unpairing_event_trigger == false ){
					//tckb 
					unpairing_flag = APP_UNPARING_CLEAR_PAIR;
					if(APP_SOUND_ON == sound_mode ){							
						add_beep(APP_UNPAIR_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);					
					}				
				}
				unpairing_event_trigger = true;
			}else{
				unpairing_flag = APP_UNPARING_KEEP_PAIR;
				unpairing_event_trigger = false;
			}
			if( app_keyb_data.keys_value.plus_key.state == KEY_CONDITION_PRESSED )
			{ 				
				if( app_param_adj_timer_expired(&app_keyb_data.keys_value.plus_key) )
				{	
					if( stand_by_temp_ind_state == STAND_BY_TEMP_IND_NONE )
					{
						stand_by_ind_temp = temp_get_val();						
						stand_by_temp_ind_state = STAND_BY_TEMP_IND_CUR_TEMP;
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.plus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}
						
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);
					}else if( stand_by_temp_ind_state == STAND_BY_TEMP_IND_CUR_TEMP ){
						stand_by_temp_ind_state = STAND_BY_TEMP_IND_SET_TEMP;
						stand_by_ind_temp = app_add_temp( stand_by_ind_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C, APP_TEMP_ADJ_STEP_C );						
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);						
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.plus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}						
					}else{
						stand_by_ind_temp = app_add_temp( stand_by_ind_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C, APP_TEMP_ADJ_STEP_C );						
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);						
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.plus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}												
					}		
				}
			}
			if( app_keyb_data.keys_value.minus_key.state == KEY_CONDITION_PRESSED )
			{ 
				if( app_param_adj_timer_expired(&app_keyb_data.keys_value.minus_key) )
				{
					if( stand_by_temp_ind_state == STAND_BY_TEMP_IND_NONE )
					{
						stand_by_ind_temp = temp_get_val();
						stand_by_temp_ind_state = STAND_BY_TEMP_IND_CUR_TEMP;
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.minus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}						
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);							
					}else if( stand_by_temp_ind_state == STAND_BY_TEMP_IND_CUR_TEMP ){
						stand_by_temp_ind_state = STAND_BY_TEMP_IND_SET_TEMP;						
						stand_by_ind_temp = app_sub_temp( stand_by_ind_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C );											
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.minus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);						
					}else{
						stand_by_ind_temp = app_sub_temp( stand_by_ind_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C );											
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.minus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}
						keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
						app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);												
					}
				}
			}
			
			//transition
			
			if( app_keyb_data.keys_value.plus_key.key_timer > KEY_POLLING_TIMER_S( APP_PAIRING_ENTER_TIME_S ) )				
			{
				app_state	= APP_PAIRING;
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
				}
			}			
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.pwr_key.key_trigger ){ 
				keep_warm_enable = FALSE;
				app_state	= APP_BOILING_PROC;
				setting_keepwarm_temp = APP_KEEPWARM_DEFAULT_TEMP_C;
				if( APP_SOUND_ON == sound_mode  ){
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);
				}
			}			
			if( ( 0 != keep_warm_temp_set_time_downcounter_s )
				&& ( stand_by_temp_ind_state == STAND_BY_TEMP_IND_SET_TEMP ) )
			{
				if( KEY_SHORT_PRES == app_keyb_data.keys_value.preheat_key.key_trigger ){ 
					setting_keepwarm_temp = stand_by_ind_temp;
					app_state	= APP_KEEPWARM_PROC;
					if( APP_SOUND_ON == sound_mode  ){
						add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);
					}				
				}										
			}
			break;
		}

		/** Состояние не используется т.к. невозможно определить пустой чайник*/
		/*case APP_EMPTY_KETTLE:
		{
			//indication
			appl_show_empty_kettle();
			//work
			heating_process_off();
			//transition
			if( temp_get_val() < TEMP_KETTLE_NOT_EMPTY_C ){
				app_state	= APP_STAND_BY;
			}
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.pwr_key.key_trigger ){ 
				if( APP_SOUND_ON == sound_mode  ){
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);
				}
				app_state	= APP_STAND_BY;
			}			
			break;
		}*/
		
		case APP_PAIRING:
		{		
			static bool paring_timeout_ind_flag = 0;
			appl_show_pairing();
			if( pairing_flag == APP_CLEAR_PARING ){
				if( paring_timeout_ind_flag == 0 ){
					paring_timeout_ind_flag = 1;
					app_timeout_timers_restart(APP_PAIRING_IND_TIMEOUT_S);				
				}
			}
			//work
			heating_process_off();
			//waiting for R4S reset unpairing_flag				
			if( app_simple_timeout_flag ){
				pairing_flag = APP_MAINTAIN_PARING;
				paring_timeout_ind_flag = 0;
				app_state = APP_STAND_BY;
				break;
			}			
			break;
		}
		
		case APP_BOILING_PROC:
		{
			//indication
			if( ( 0 != keep_warm_temp_set_time_downcounter_s )
				&&( TRUE == keep_warm_enable ) )
			{
				if( (app_keyb_data.keys_value.plus_key.state == KEY_CONDITION_PRESSED) || (app_keyb_data.keys_value.minus_key.state == KEY_CONDITION_PRESSED) )
				{								
					app_show_boiling(keep_warm_enable, setting_keepwarm_temp, FALSE);
				}else{
					app_show_boiling(keep_warm_enable, setting_keepwarm_temp, TRUE);
				}
			}else{
				app_show_boiling(keep_warm_enable, APP_HEATING_TEMP_C, TRUE);				
			}
			//work
			if(	( temp_is_water_boil() )
				&&( 0 == boiling_min_time_downcounter_s ))
			{
				boiling_complete_flag = TRUE;
			}			
			if( ( boiling_complete_flag == TRUE )
				&& ( boiling_complete_flag_prew == FALSE ) )
			{
				//звуковая индикация завершения кипечения
				if( APP_SOUND_ON == sound_mode  )
				{					
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
				}
			}
			boiling_complete_flag_prew = boiling_complete_flag;
			
			if( boiling_complete_flag == TRUE ){
				heating_force_off();
			}else{
				heating_force_on(HEATER_MAX_PWM_DUTY_CICLE);
			}
			//controll
			if( TRUE == keep_warm_enable )
			{				
				if( app_keyb_data.keys_value.plus_key.state == KEY_CONDITION_PRESSED )
				{ 				
					if( app_param_adj_timer_expired(&app_keyb_data.keys_value.plus_key) )
					{		
						//если истек таймаут изменения параметра
						if( setting_keepwarm_temp != app_add_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C, APP_TEMP_ADJ_STEP_C ) )
						{							
							//если уставка изменилась
							setting_keepwarm_temp = app_add_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C );
							if( KEY_SHORT_PRES == app_keyb_data.keys_value.plus_key.key_trigger ){
								if( APP_SOUND_ON == sound_mode  ){				
									add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
								}
							}
							keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
							app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);												
						}
					}
				}
				if( app_keyb_data.keys_value.minus_key.state == KEY_CONDITION_PRESSED )
				{ 
					if( app_param_adj_timer_expired(&app_keyb_data.keys_value.minus_key) )
					{
						//если истек таймаут изменения параметра
						if( setting_keepwarm_temp != app_sub_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C ) )
						{ 
							//если уставка изменилась
							setting_keepwarm_temp = app_sub_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C );											
							if( KEY_SHORT_PRES == app_keyb_data.keys_value.minus_key.key_trigger ){
								if( APP_SOUND_ON == sound_mode  ){				
									add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
								}
							}
							keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
							app_timeout_timers_restart(APP_TEMP_PRESET_TIMEOUT_S);							
						}
					}
				}
			}
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.preheat_key.key_trigger ){ 				
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
				}
				if( TRUE == keep_warm_enable ){
					keep_warm_enable = FALSE;
					keep_warm_temp_set_time_downcounter_s = 0;					
				}else{
					keep_warm_enable = TRUE;
					keep_warm_temp_set_time_downcounter_s = APP_KEEP_WARM_SET_TIME_S;
				}
			}
			//transition
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.pwr_key.key_trigger ){ 
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
				}
				if( TRUE == keep_warm_enable ){
					app_state	= APP_KEEPWARM_PROC;
				}else{
					app_state	= APP_STAND_BY;
				}
			}
			/** Состояние не используется т.к. невозможно определить пустой чайник*/
			/*if( temp_is_empty_kettle() ){
				heating_force_off();
				app_state	= APP_EMPTY_KETTLE;						
			}*/
			if( ( boiling_complete_flag == TRUE )
				&& ( 0 == boinig_complete_ind_time_downcounter_s ) )
			{
				if( TRUE == keep_warm_enable ){
					app_state	= APP_KEEPWARM_PROC;
				}else{
					app_state	= APP_STAND_BY;
				}
			}					
			break;
		}		
		case APP_KEEPWARM_PROC:
		{
			//indication
			if( keep_warm_boost_complete_flag )
			{
				app_show_keepwarm(setting_keepwarm_temp, FALSE, keep_warm_boost_complete_flag);
			}else{
				app_show_keepwarm(setting_keepwarm_temp, TRUE, keep_warm_boost_complete_flag);
			}
			//work
			if( KEEP_WARM_CTRL_BOOST == keep_warm_ctrl_type ){
				boost_state = boost_proc( temp_get_float_val(), (float)setting_keepwarm_temp );
				if( BOOST_FINISHED == boost_state ){
					keep_warm_ctrl_type = KEEP_WARM_CTRL_REG;
				}
			}else{
				keep_warm_ctrl_type = keep_warm_get_ctrl_type(  temp_get_val(), setting_keepwarm_temp );
				if( KEEP_WARM_CTRL_BOOST == keep_warm_ctrl_type ){
					//при переходе из регулятора в разгон инициализируем алгоритм заново 
					boost_proc_init();
				}else{
					heating_process_on( simple_reg( temp_get_val() , setting_keepwarm_temp, 2 ) );				
				}
			}
			if( fabs( setting_keepwarm_temp - temp_get_val() ) < APP_KEEPWARM_TEMP_DIFFERENCE_BOOST_IS_OVER_C ){
				keep_warm_boost_complete_flag = TRUE;
			}			
			if(	( FALSE == keep_warm_boost_complete_flag_prew )
				&&( TRUE == keep_warm_boost_complete_flag ) )
			{
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);	
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_MULTI);
				}
				app_sec_timer_reset();
			}
			keep_warm_boost_complete_flag_prew = keep_warm_boost_complete_flag;
			//controll
			if( app_keyb_data.keys_value.plus_key.state == KEY_CONDITION_PRESSED )
			{ 				
				if( app_param_adj_timer_expired(&app_keyb_data.keys_value.plus_key) )
				{		
					//если истек таймаут изменения параметра
					if( setting_keepwarm_temp != app_add_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C, APP_TEMP_ADJ_STEP_C ) )
					{
						//если уставка изменилась
						setting_keepwarm_temp = app_add_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C, APP_TEMP_ADJ_STEP_C );
						keep_warm_ctrl_type = keep_warm_get_ctrl_type(  temp_get_float_val(), (float)setting_keepwarm_temp );
						if( keep_warm_ctrl_type == KEEP_WARM_CTRL_BOOST ){
							//если остались в алгоритме разгона обновить уставку
							boost_proc_setting_update( temp_get_val(), setting_keepwarm_temp );
						}
						keep_warm_boost_complete_flag = FALSE;
						keep_warm_boost_complete_flag_prew = FALSE;
						keep_warm_time_downcounter_s = APP_KEEPWARM_MAX_TIME_S;
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.plus_key.key_trigger ){
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}
					}
				}
			}
			if( app_keyb_data.keys_value.minus_key.state == KEY_CONDITION_PRESSED )
			{ 
				if( app_param_adj_timer_expired(&app_keyb_data.keys_value.minus_key) )
				{
					//если истек таймаут изменения параметра
					if( setting_keepwarm_temp != app_sub_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C ) )
					{
						//если уставка изменилась
						setting_keepwarm_temp = app_sub_temp( setting_keepwarm_temp, APP_KEEPWARM_MIN_TEMP_C, APP_KEEPWARM_MAX_TEMP_C,  APP_TEMP_ADJ_STEP_C );
						keep_warm_ctrl_type = keep_warm_get_ctrl_type(  temp_get_float_val(), (float)setting_keepwarm_temp );
						if( keep_warm_ctrl_type == KEEP_WARM_CTRL_BOOST ){
							//если остались в алгоритме разгона обновить уставку
							boost_proc_setting_update( temp_get_val(), setting_keepwarm_temp );
						}
						keep_warm_boost_complete_flag = FALSE;
						keep_warm_boost_complete_flag_prew = FALSE;
						keep_warm_time_downcounter_s = APP_KEEPWARM_MAX_TIME_S;
						if( KEY_SHORT_PRES == app_keyb_data.keys_value.minus_key.key_trigger )
						{
							if( APP_SOUND_ON == sound_mode  ){				
								add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
							}
						}
					}
				}
			}			
			//transition
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.pwr_key.key_trigger ){ 				
				heating_force_off();			//если выходим из режима обязательно выключить нагреватель
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
				}
				app_state	= APP_BOILING_PROC;
				
			}
			if( KEY_SHORT_PRES == app_keyb_data.keys_value.preheat_key.key_trigger ){ 
				heating_force_off();	//если выходим из режима обязательно выключить нагреватель
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
				}
				app_state	= APP_STAND_BY;
			}

			if( 0 == keep_warm_time_downcounter_s ){
				app_state	= APP_STAND_BY;
				if( APP_SOUND_ON == sound_mode  ){				
					add_beep(APP_SHORT_BEEP_MS, APP_BEEP_PAUSE_MS, BEEP_SINGLE);				
				}				
			}
			break;
		}
		
		case APP_ERROR:
		{
			//indication
			app_show_error(app_error);
			//work
			heating_process_off();
			//transition
			//no transition
			break;
		}
		
		case APP_TEST:
		{
			uint8_t test_finished;
			test_finished = test_proc();
			if( ( test_finished == 1 )
				|| ( app_simple_timeout_flag ) )
			{
				app_state = APP_STAND_BY;
			}				
			break;
		}
		
		default:
		{
			app_state = APP_STAND_BY;			
			break;
		}
	}
	
}
/*
*  @brief 	Возвращает версия ПО
*
*  @param	none
*  @return 	версия ПО
*
*/
unsigned int Appl_GetVersion(void){
	return SOFTWARE_VERSION;
}
/*
*  @brief 	Возвращает текущую температуру
*
*  @param	none
*  @return 	текущая температура
*
*/
unsigned char Appl_GetTemp( void ){
	unsigned char temp;
	switch( app_state )
	{
		case  APP_BOILING_PROC:
		{
			/** Если находимся в режиме кипечения отображаем текущую температуру или 100 когда процесс кипечения завершен.
			*/
			/*if( boiling_complete_flag == true )
			{
				return 100;
			}else{*/
				temp = temp_get_val();
			//}
			break;
		}
		
		case APP_KEEPWARM_PROC:
		{
			/** Если находимся в режиме подогрева отображаем текущую температуру или температуру уставки если выход на заданную температуру завершен.
			*/		
			if( keep_warm_boost_complete_flag == true )
			{
				temp = setting_keepwarm_temp;
			}else{
				temp = temp_get_val();
			}
			break;
		}
		
		default :
		{
			temp = temp_get_val();
			break;
		}
	}	
	if( temp > 100 )temp = 100;
	return temp;
}
/*
*  @brief 	Возвращает температуру уставки поддержания температуры
*
*  @param	none
*  @return 	уставка поддержания температуры
*
*/
unsigned char Appl_GetSetTemp(void){
	if( keep_warm_enable == true )
	{
		//если подогрев разрешен возвращаем уставку
		return setting_keepwarm_temp;	
	}else{
		//если запрещен возвращам 0 - признак запрета подогрева
		return 0;
	}
}
/*
*  @brief 	Возвращает время до окончания поддержания температуры
*
*  @param	none
*  @return 	время до окончания поддержания температуры
*
*/
unsigned int Appl_GetTime(void){
	return (keep_warm_time_downcounter_s/60);	//приводим к минутам
}
/*
*  @brief 	Возвращает ошибку
*
*  @param	none
*  @return 	время ошибку
*
*/
unsigned char Appl_GetError(void){
	switch( app_error ){
		case ERR_NO:
			return APPL_NO_ERR;		
		
		case ERR_OVERHEAT:
			return APPL_OVERHEAT_ERR;		
		
		default:
			return APPL_NO_ERR;			
	}
}
/*
*  @brief 	Устанавливает температуру поддержания
*
*  @param[in]	температура для установки
*  @return 		TRUE - температура установлена. FALSE - температура не установлена
*
*/
bool Appl_SetTemp(uint8_t temp){
	if( APP_KEEPWARM_PROC == app_state )
	{
		if( ( temp <= APP_KEEPWARM_MAX_TEMP_C )
			&&( temp >= APP_KEEPWARM_MIN_TEMP_C ))
		{		
			keep_warm_time_downcounter_s = APP_KEEPWARM_MAX_TIME_S;
			keep_warm_boost_complete_flag = false;
			keep_warm_boost_complete_flag_prew = false;
			setting_keepwarm_temp = temp;
			return true;
		}else{
			if( 0 == temp )
			{				
				keep_warm_enable = FALSE;
				app_state = APP_STAND_BY;
				return true;
			}
		}
	}
	if( APP_BOILING_PROC == app_state )
	{
		if( ( temp <= APP_KEEPWARM_MAX_TEMP_C )
			&&( temp >= APP_KEEPWARM_MIN_TEMP_C ))
		{					
			setting_keepwarm_temp = temp;
			keep_warm_enable = TRUE;
			return true;
		}else{
			if( 0 == temp )
			{
				keep_warm_enable = FALSE;
				return true;
			}
		}
	}
	return false;
}
/*
*  @brief 	Возвращает выполняемую программу
*
*  @param	none
*  @return 	выполняемая программа
*/
unsigned char Appl_GetProgram(void){
	if( app_state == APP_KEEPWARM_PROC )	
	{
		return APPL_KEEPWARM_PROG;
	}else{
		return APPL_BOILING_PROG;
	}
}
/*
*  @brief 	Устанавливает программу для выполнения. Переводит state machine в соответсвуещее состояние
*
*  @param[in]	prog - программа для выполнения
*  @return 		TRUE - программа установлена. FALSE - программа не установлена
*/
bool Appl_SetProgram(unsigned char prog){
	if( app_state == APP_STAND_BY ){
		//запуск программы из STAND BY
		switch(prog)
		{
			case APPL_BOILING_PROG:
			{
					setting_keepwarm_temp = APP_KEEPWARM_DEFAULT_TEMP_C;
					keep_warm_enable = FALSE;
					app_state	= APP_BOILING_PROC;
					return true;
				
			}
			case APPL_KEEPWARM_PROG:
			{			
					setting_keepwarm_temp = APP_KEEPWARM_DEFAULT_TEMP_C;
					app_state	= APP_KEEPWARM_PROC;
					return true;
			}			
			default:
			{
				return false;
			}
		}
	}
	if( app_state == APP_KEEPWARM_PROC ){
		//запуск программы из APP_KEEPWARM_PROC
		if( APPL_BOILING_PROG == prog )
		{
			app_state	= APP_BOILING_PROC;	
			return true;
		}
		if( APPL_KEEPWARM_PROG == prog )
		{
			return true;
		}
	}
	if( app_state == APP_BOILING_PROC ){
		//запуск программы из APP_KEEPWARM_PROC
		if( APPL_KEEPWARM_PROG == prog )
		{
			app_state	= APP_KEEPWARM_PROC;
			return true;
		}
		if( APPL_BOILING_PROG == prog )
		{
			keep_warm_enable = TRUE;
			return true;
		}
	}
	return false;	
}
/*
*  @brief 	Останавливает выполняемую программу.
*
*  @param	none
*  @return 	TRUE - программа остановлена. FALSE - программа не остановлена
*/
bool Appl_Stop(void){	
	if( ( app_state == APP_BOILING_PROC ) 
		|| ( app_state == APP_KEEPWARM_PROC ) )
	{
		app_state = APP_STAND_BY;
		return true;
	}
	return false;
}
/*
*  @brief 	Возвращает текущее состояние устройства
*
*  @param	none
*  @return 	текущее состояние устройства
*/
unsigned char Appl_GetState(void){
	switch( app_state ){		
		case APP_STAND_BY:
		case APP_START:			
			return APPL_BEGIN_STATE;
		
		case APP_PAIRING:
			return APPL_PAIRING_STATE;
		
		case APP_BOILING_PROC:
		case APP_KEEPWARM_PROC:
			return APPL_PROGRAM_STATE;	
			
		case APP_ERROR:
			return APPL_ERROR_STATE;
		
		default:
			return 0xFF;
	}
}
/*
*  @brief 	Возвращает текущее состояние поддержания
*
*  @param	none
*  @return 	TRUE - поддержание включено. FALSE - поддержание вsключено.
*/
bool Appl_GetPreheat(void){
	return keep_warm_enable;
}
/*
*  @brief 	Возвращает состояние запроса от пользователя на unparing всех устройств
*
*  @param	none
*  @return 	TRUE - запрос на unparing всех устройств. FALSE - нет запроса на unparing всех устройств
*/
bool Appl_IsUnpairing(void){
	return unpairing_flag;		
}
/*
*  @brief 	Обработчик ошибок. Переводити state mashine в состояние APP_ERROR
*
*  @param[in]	Err - ошибка
*  @return 		none
*/
void Appl_ErrorHandler(error_type_t Err){
	app_error	= Err;
	app_state = APP_ERROR;
	return;
}
/*
*  @brief 	Запрос от R4S на выход из режима paring
*
*  @param		none
*  @return 		none
*/
void Appl_ClrPairing(void){	
	pairing_flag = APP_CLEAR_PARING;
	return;
}
/*
*  @brief 	Возвращает состояние блокировки кнопок
*
*  @param		none
*  @return 		состояние блокировки
*/
bool Appl_GetBlock( void ){
	return 0;
}
/*
*  @brief 	Устанавливает состояние блокировки кнопок
*
*  @param[in]	block - состояние блокировки
*  @return 		TRUE - состояние блокировки установленно. FALSE - не установленно. 
*/
bool Appl_SetBlock( bool block ){
	/*if( APP_STAND_BY != app_state )return false;
	app_panel_block_flag = block;
	if( false == app_panel_block_flag ){
		block_ind_time_downcounter_s = 0;
	}*/
	return false;
}
Made on
Tilda