KEY EVENT & STATE 구현
2007. 1. 25
PLATFORM TEAM 정용학
2
차 례
Key Event HS TASK UI TASK LONG KEY
STATE 구현
소스코드 및 실행화면
질의 응답 및 토의
KEY EVENT - HS TASK
hs_task
hs_init
keypad_init
clk_def
clk_reg
clk_reg2
keypad_scan_keypad
keypad_pass_key_code
keypad_event_cb
ui_key_event_cb
UI TASKUI_KEY_SIG
KEY EVENT - HS TASK
clk_def clk_def( &keypad_clk_cb_keypad ); double linked list 구조call_back_ptr->prev_ptr = NULL; /* Previous and Next pointers */call_back_ptr->next_ptr = NULL; /* Previous and Next pointers */call_back_ptr->cntdown = 0; /* Countdown expiration, milliseconds */call_back_ptr->routine_ptr = NULL; /* Routine to call on expiration */call_back_ptr->crnt_ms = 0; /* This interval */call_back_ptr->next_ms = 0; /* Next interval */call_back_ptr->repeat = FALSE; /* Whether to repeat */
KEY EVENT - HS TASK
clk_reg2 clk_reg( &keypad_clk_cb_keypad, keypad_scan_keypad,
KEYPAD_POLL_TIME, KEYPAD_POLL_TIME, TRUE ); clk_reg2( call_back_ptr, (void (*)(int4,void*)) routine_ptr, ms_first, ms_periodic,
repeat, NULL );
void clk_reg2( clk_cb_type *call_back_ptr, void (*routine_ptr)( int4 ms_interval, void *user_data_ptr ), int4 ms_first, int4 ms_periodic, boolean repeat, void *usr_data_ptr) clk_cb_delete( call_back_ptr ); call_back_ptr 구조체에 각 원소 저장; clk_cb_insert( call_back_ptr );
KEY EVENT - HS TASK
clk_cb_delete (clk_cb_type *call_back_ptr )
cnt=3 cnt=2cnt=4
cnt=2
call_back_ptr
2 + 4 = 6
cnt=3 cnt=2 cnt=4
call_back_ptr
activelist
activelist
clk_tick_last
clk_tick_last
KEY EVENT - HS TASK
clk_cb_insert (clk_cb_type *call_back_ptr ) 원래 A는 3초, B는 3 + 2 = 5초 , C는 3 + 2 + 2 = 7초 X ( 6초 ) 가 삽입되는 경우 A를 지나면 6 - 3 = 3초, B와 비교하니 더 크다 B를 지나면 3 - 2 = 1초, C와 비교하니 더 작다 B와 C사이에 1초로 삽입 C는 2 - 1 = 1초로 바뀜
cnt=3 cnt=2cnt=1
cnt=2
call_back_ptr
2 - 1 = 1
activelist
clk_tick_last
A B CX
KEY EVENT - HS TASK
keypad_scan_keypad call back timer에 정의되어 있어서 20msec 마다 실행됨
if (keypad_locked) return;for (row=0; row<KEYPAD_ROWS; row++){ for (column=0; column<KEYPAD_COLUMNS; column++){ switch ( keypad_key_state[row][column] ){ case KS_KEY_UP: if ( keys_pressed[row][column] == TRUE){ keypad_key_state[row][column] = KS_DEBOUNCE; sleep_allowed = FALSE; } break; case KS_DEBOUNCE: if ( keys_pressed[row][column] == TRUE ) { KEYPAD_PASS_KEY_CODE( keys[row][column],
HS_NONE_K ); keypad_key_state[row][column] = KS_UP_WAIT; sleep_allowed = FALSE;
} else { keypad_key_state[row][column] = KS_KEY_UP;} break;case KS_UP_WAIT: if ( keys_pressed[row][column] == TRUE ) { sleep_allowed = FALSE; } else { keypad_key_state[row][column] = KS_KEY_UP; KEYPAD_PASS_KEY_CODE( HS_RELEASE_K,
keys[row][column] );} break;if (keypad_key_buffer.wr_idx != keypad_key_buffer.rd_idx ){ sleep_allowed = FALSE;}if (sleep_allowed) KEYPAD_SLEEP_ALLOW();
9
KEY EVENT - HS TASK
Key Status 각 키마다 각자 status를 가짐 KS_KEY_UP
키가 눌러져 있지 않은 상태 KS_DEBOUNCE
처음 진입 시 KEYPAD_PASS_KEY_CODE 호출 PRESSED KEY
KS_UP_WAIT 키가 떼어질 경우 KEYPAD_PASS_KEY_CODE 호출 RELEASE_KEY
KS_DEBOUNCE 있는 이유 hardware 상의 noise 때문
KS_KEY_UP KS_DEBOUNCE
KS_UP_WAIT
KEY EVENT - HS TASK
keypad_pass_key_code ( byte key_code, byte key_parm ) MULTI_KEY 정의 시 매개변수 2개if ( keypad_event_cb ) {#ifdef FEATURE_KEYPAD_MULTI_KEY keypad_key_event_type key_event; key_event.key_code = key_code; key_event.key_parm = key_parm;#else kpd_key_event_type key_event; key_event.key_code = key_code;#endif keypad_event_cb ( key_event ); if(keypad_debug) keypad_key_buffer 에 저장 ;} else keypad_key_buffer 에 저장 ;
keypad_event_cb는 처음에 NULL값을가지므로 callback fuction 이 없을 경우는해당 키값을 keypad_key_buffer에 저장하고callback fuction 이 있을 경우는 해당 함수를실행한다
LOCAL keypad_key_event_cb_f_type *keypad_event_cb = NULL;
KEY EVENT - HS TASK
keypad_event_cb 처음에는 NULL 값을 가짐 UI 내에서 keypad_register 함수로 인해 ui_key_event_cb라는 함수를 가리킴void ui_key_event_cb( kpd_key_event_type key_event){ if (((ui_key_buffer.wr_idx + 1) & UI_KEY_BUF_MASK) != ui_key_buffer.rd_idx) { ui_key_buffer.data [ ui_key_buffer.wr_idx ] = key_event.key_code; ui_key_buffer.wr_idx = (ui_key_buffer.wr_idx+1) & UI_KEY_BUF_MASK; }
(void) rex_set_sigs( &ui_tcb, UI_KEY_SIG );}
KEY EVENT - HS TASK
rex_set_sigs rex_set_sigs( &ui_tcb, UI_KEY_SIG );
rex_sigs_type rex_set_sigs( rex_tcb_type *p_tcb, rex_sigs_type p_sigs){ p_tcb->sigs = p_tcb->sigs | p_sigs; if((p_tcb->wait & p_sigs) != 0) { p_tcb->wait = 0; if (( p_tcb->pri > rex_best_task->pri) && REX_TASK_RUNNABLE ( p_tcb )) { rex_best_task = p_tcb; rex_sched(); } }}
KEY EVENT - UI TASK
ui_task
ui_init
uikey_init
kpd_suv_srvc
kpd_reg_key_event
keypad_register
rex_wait
ui_signal
handle_keys
get_key
handle_hs_keys
ui_add_event
ui_do_event
for ( ; ; )
KEY EVENT - UI TASK
boolean kpd_suv_srvc ( kpd_cb_f_handle_type *kpd_cb_func) kpd_sub_srvc (ui_kpd_sub_cb); keypad service를 subscribe 하기 위해서 사용됨kpd_handle_type kpd_handle;
if( kpd_subscribed ) return FALSE;
kpd_handle.id = HS_KPD_DEVICE; kpd_handle.status = HS_SUBSCRIBED;
kpd_cb_func ( kpd_handle ); kpd_subscribed = TRUE;
return TRUE;
void ui_kpd_sub_cb( kpd_handle_type kpd_handle ){ ui_kpd_handle = kpd_handle;}
KEY EVENT - UI TASK
kpd_reg_key_event kpd_reg_key_event ( ui_kpd_handle, ui_key_event_cb ); keypad_register 함수 호출 후에 simulation
boolean kpd_reg_key_event ( kpd_handle_type kpd_handle, kpd_cb_f_key_event_type *kpd_cb_func ){ keypad_register(kpd_cb_func);
// simulate.. while (( key.key_code = keypad_get_key() ) != HS_NONE_K) { kpd_cb_func ( key ); }}
KEY EVENT - UI TASK
keypad_register keypad_register (kpd_cb_func); kpd_cb_fubc 는 ui_key_event_cb
#ifdef FEATURE_KEYPAD_MULTI_KEYextern boolean keypad_register( keypad_key_event_cb_f_type *cb)#elseextern boolean keypad_register( kpd_cb_f_key_event_type *cb)#endif
{ if ( keypad_event_cb ) return FALSE; keypad_event_cb = cb; return TRUE;}
keypad_event_cb는 처음에 NULL값
LOCAL keypad_key_event_cb_f_type *keypad_event_cb = NULL;
KEY EVENT - UI TASK
rex_wait 해당 signal이 들어오면 진행중인 signal을 wait상태로 보내고 scheduling 해당 signal이 안들어오면 계속 suspend 상태rex_sigs_type rex_wait( rex_sigs_type p_sigs){ rex_sigs_type sigs = 0; if( (rex_curr_task->sigs & p_sigs) == 0 ) { rex_curr_task->wait = p_sigs; rex_set_best_task ( REX_TASK_LIST_FRONT() ); rex_sched(); } sigs = rex_curr_task->sigs; return sigs;}
KEY EVENT - UI TASK
ui_signal 현재 signal에 맞는 함수를 수행
rex_clr_sigs 해당 bit를 0으로 set
void ui_signal ( rex_sigs_type sigs, q_type *ui_cmd_q_ptr){ ...... if( sigs & UI_KEY_SIG ) { (void) rex_clr_sigs( rex_self(), UI_KEY_SIG )
; handle_keys(); } }
rex_sigs_type rex_clr_sigs( rex_tcb_type *p_tcb, rex_sigs_type p_sigs) { rex_sigs_type prev_sigs = 0;
p_sigs = ~p_sigs; prev_sigs = p_tcb->sigs; p_tcb->sigs = p_tcb->sigs & p_sigs; return prev_sigs;}
KEY EVENT - UI TASK
handle_keys get_key 함수를 이용해 key 값을 받아와서 handle_hs_key함수를 호출while ( ( key = (hs_key_type) get_key()) != HS_NONE_K ) { ... handle_hs_key (key);}
byte get_key( void ){ byte keycode; if ( ui_key_buffer.wr_idx == ui_key_buffer.rd_idx ) { keycode = HS_NONE_K; } else { keycode = ui_key_buffer.data [ ui_key_buffer.rd_idx ]; ui_key_buffer.rd_idx = (ui_key_buffer.rd_idx+1) & UI_KEY_BUF_MASK; } return( keycode );}
KEY EVENT - UI TASK
handle_hs_key 키값에 따라서 이벤트를 설정하고 ui_add_event 실행
void handle_hs_key ( hs_key_type key ){ ..... if( key != HS_RELEASE_K && key != HS_PWR_K ) { ui_add_event( (word)key ); }}
void ui_add_event ( word event ){ if( ( ui_event.head+1 ) % UI_EVENT_SIZE != ui_event.tail ) { ui_event.head = ( ui_event.head+1 ) % UI_EVENT_SIZE; ui_event.buf [ ui_event.head ] = event; }}
KEY EVENT - UI TASK
ui_do_event event 있을 경우 실행ui_task(){ for( ; ; ) { .... while ( ui.getkeys && ( ui_event.head != ui_event.tail )) { ui_event.tail = ( ui_event.tail+1 ) % UI_EVENT_SIZE; ui_do_event ( ui_event.buf [ ui_event.tail ] ); } }}
KEY EVENT - LONG KEY
Long key 처리 keypad_scan_keypad 함수가 20msec 마다 호출이 되는 상태에서
keypad_pass_key_code 호출if (keypad_locked) return;for (row=0; row<KEYPAD_ROWS; row++){ for (column=0; column<KEYPAD_COLUMNS; column++){ switch ( keypad_key_state[row][column] ){ case KS_KEY_UP: if ( keys_pressed[row][column] == TRUE){ keypad_key_state[row][column] = KS_DEBOUNCE; sleep_allowed = FALSE; } break; case KS_DEBOUNCE: if ( keys_pressed[row][column] == TRUE ) { KEYPAD_PASS_KEY_CODE( keys[row][column],
HS_NONE_K ); keypad_key_state[row][column] = KS_UP_WAIT; sleep_allowed = FALSE;
KEY EVENT - LONG KEY
handle_hs_key keypad_pass_key_code 함수를 통해 받은 key값을 handle_hs_key에서 처리 ui_keycb_reg 에서 clk_reg호출해서 cbtimer 에 등록switch( key ) { case HS_0_K: .... case HS_9_K: if ( !ui.onetouch && ((int)key >= (int)'1' && (int)key <= (int)'9')) { ui_keycb_reg( (int4)NO_ONETOUCH_TIME, (int4 )0, FALSE ); } key_down_key = (word)key; }
void ui_keycb_reg( int4 ms_first, int4 ms_periodic, boolean repeat ){ clk_reg( &key_cb, ui_key_cb, ms_first, ms_periodic, repeat);}
2500
KEY EVENT - LONG KEY
2500msec 안에 키를 뗄 경우 (KS_KEY_UP) keypad_scan_keypad 함수에서 KS_KEY_UP상태로 되면서 HS_RELEASE_K 를
KEYPAD_PASS_KEY_CODE 인자로 넘겨준다
case KS_UP_WAIT: if ( keys_pressed[row][column] == TRUE ) { sleep_allowed = FALSE; } else { keypad_key_state[row][column] = KS_KEY_UP; KEYPAD_PASS_KEY_CODE( HS_RELEASE_K, keys[row][column] ); } break;
KEY EVENT - LONG KEY
handle_hs_key keypad_pass_key_code 함수를 통해 받은 HS_RELEASE_K 처리 ui_key_cb_dereg 함수로 callback timer에서 제거한다switch( key ) { .... case HS_RELEASE_K: key_down_key = (word)HS_NONE_K; ui_keycb_dereg(); break; ....}
void ui_keycb_dereg( void ){ clk_dereg( &key_cb ); }
void clk_dereg ( clk_cb_type *call_back_ptr ){ clk_cb_delete( call_back_ptr );}
KEY EVENT - LONG KEY
2500msec 안에 키를 안 뗄 경우 ( KS_UP_WAIT ) 2500msec가 지나면 등록해 놓은 callback timer에 의해 ui_key_cb 함수 호출 timeflags 에 UI_KEY_TIMER set, UI_TIMER_SIG set
void ui_keycb_reg( int4 ms_first, int4 ms_periodic, boolean repeat ){ clk_reg( &key_cb, ui_key_cb, ms_first, ms_periodic, repeat);}
static void ui_key_cb ( int4 interval ){ ui_set_sigs (&timeflags, UI_KEY_TIMER); (void) rex_set_sigs( &ui_tcb, UI_TIMERS_SIG );}
static void ui_set_sigs (word* bit_mask, word or_mask){ INTLOCK(); *bit_mask |= or_mask; INTFREE();}
KEY EVENT - LONG KEY
ui_signal timerflags를 검사해서 해당 event 수행 rex_clr_sigs 로 UI_TIMER_SIG unset
void ui_signal ( rex_sigs_type sigs, q_type *ui_cmd_q_ptr ){ if( sigs & UI_TIMERS_SIG ) { handle_timers(); (void) rex_clr_sigs( &ui_tcb, UI_TIMERS_SIG ); } ....}
KEY EVENT - LONG KEY
handle_timers ui_do_event 실행 clk_dereg 실행if( timeflags & UI_KEY_TIMER ) { ui_clear_sigs (&timeflags, (word) ~UI_KEY_TIMER); switch( key_down_key ) { case HS_1_K: .... case HS_0_K: ui.wasonetouch = TRUE; ui_do_event( (word)UI_DIGDOWN_F ); key_down_key = (word)UI_DIGDOWN_F; clk_dereg( &key_cb ); UI_ENABLE_SLEEP(); break;
...
}
STATE 구현
KEY TASKinput key event
hs_key_thread()
ALARM TASKalarm check
alarm_thread()
UI TASKstate machine
ui_task()
SetEvent 제어
STATE 구현 - ALARM TASK
while(1){ current_date = get_current_date(); current_time = get_current_time(); temp = my_schedule->head; while( temp != NULL ){ schedule_date = atoi(temp->date); schedule_time = atoi(temp->time); if( current_date == schedule_date && current_time == schedule_time) { puts("\n\n\nAlarm\a"); puts("========================================="); printf("1.Date = %s\n",temp->date); printf("2.TIME = %s\n",temp->time); printf("3.Type = %s\n",temp->kind); printf("4.Memo = %s\n",temp->memo); puts("=========================================\n\n"); } temp = temp->link; } sleep(60000);
alarm_thread
31
STATE 구현 - KEY TASK
4 5 .... 31302928273210
rd_idx
wr_idx
typedef struct { byte rd_idx; /* read index */ byte wr_idx; /* write index */ byte data [ UI_KEY_BUF_SIZE ]; /* data buffer */} ui_key_buffer
STATE 구현 - KEY TASK
void hs_key_thread( void* arg ){ while ( TRUE ) { WaitForSingleObject( hKeyEvent, INFINITE); key_buffer.data[key_buffer.wr_idx] = getch(); key_buffer.wr_idx = (key_buffer.wr_idx+1) & UI_KEY_BUF_MASK; rex_set_sigs(UI_KEY_SIG); }}
while { ui_do_event( ); SetEvent( hKeyEvent );}
Write
SetEvent
sigs = sigs | p_sigs;
rex_set_sigs
33
STATE 구현 - UI TASK
4 5 .... 31302928273210
tail
head
typedef struct { int head; int tail; word buf[ UI_EVENT_SIZE ];} ui_event_type;
STATE 구현 - UI TASK
ui_init(); scheduler_init();
for( ;; ) {
ui_signal(sigs); if ( ui_event.head != ui_event.tail ) { ui_event.tail=( ui_event.tail+1 )%UI_EVENT_SIZE; ui_do_event( ui_event.buf[ ui_event.tail ] ); SetEvent(hKeyEvent); } else ui_do_event( UI_NONE_F );
if( !ui.pwr ) break;}
view_del_total(DEL);
ui_taskui_maj_type new_state;ui.event = (int)in_event; do { switch( state ) { case UI_STARTUP_S: new_state = uistate_startup(); break; case UI_IDLE_S: new_state = uistate_idle(); break; case ... ... default : } push / pop 연산
} while ( new_state != UI_NOSTATE_S );
ui_do_event
STATE 구현 - UI TASK
if( (p_sigs & UI_KEY_SIG) != 0){ rex_clr_sigs(UI_KEY_SIG); handle_keys();}
ui_signal
p_sigs = ~p_sigs;sigs = sigs & p_sigs;
rex_clr_sigs
hs_key_type key;while ( ( key = (hs_key_type)get_key()) != HS_NONE_K ){ handle_hs_key(key);}
handle_keys
ui_add_event( (word)key );
handle_hs_key
if (key_buffer.wr_idx == key_buffer.rd_idx) { keycode = HS_NONE_K;} else { keycode = key_buffer.data[key_buffer.rd_idx]; key_buffer.rd_idx = (key_buffer.rd_idx+1) & UI_KEY_BUF_MASK;}
get_key()
if( ( ui_event.head+1 ) % UI_EVENT_SIZE != ui_event.tail ) { ui_event.head = ( ui_event.head+1 ) % UI_EVENT_SIZE; ui_event.buf[ ui_event.head ] = event; }
ui_add_event
36
소스코드 및 실행화면
37
질의 응답 및 토의