NEC IR Format은 일반적으로 사용하는 IR 리모컨의 통신 프로토콜이다.
이를 이용하기 위해서는 다음과 같은 IR 수신부가 있어야 한다.
NEC IR Format의 전체적은 프로토콜은 다음과 같다.
이 포맷에서 1과 0은 다음과 같이 확인한다.
즉 리더 코드 후에 36개의 data코드를 확인하면 된다.
//예제 소스 시작
예제 소스에서는 인터럽트의 모든 엣지에서 인터럽트가 걸리기 때문에 High와 Low로 유지되는 시간 모두를 체크한다.
하지만 대부분의 예제 소스(소스포지 등)에서는 라이징 엣지에서만 체크를 한다. 실은 이 경우가 더 쉬운데 현재 로봇의 (PIC32)의 인터럽트 포트가 아니라 인풋캡쳐 포트에 연결이 되어 있기 때문이다. 아이러니 한 것은 원래 IR 리시버가 인터럽트 포트에 연결이 되어 있었는데 인터럽트 포트는 하나의 엣지 밖에 인터럽트를 못 발생시키기 때문에 회로를 변경 한 것이다. 조금 더 먼저 알았다면 회로도 안바꿔도 되고, 더 쉽게 소스를 짤 수 있었틀 텐데..
// 리모컨 NEC format의 신호 타임 정의 _________________________________________ #define NEC_LEADER_CODE_HIGH 141 // 9000us / 64us = 141 (64us는 timer0 주기) #define NEC_LEADER_CODE_LOW 70 // 4500us / 64us = 70 #define NEC_BIT_START 9 // 562.5us / 64us = 9 #define NEC_BIT_LOW 9 // 562.5us / 64us = 9 #define NEC_BIT_HIGH 26 // 1687.5us / 64us = 26 Bool IsNEC( Uint32 diff, Uint32 type) { #define NEC_ROUNDOFF 6 if( absI ( diff - type ) <= NEC_ROUNDOFF ) return True; else return False; } #define NEW_NEC 1 #define OLD_NEC 0 #define NEC_NONE 0 #define NEC_SHORT 1 #define NEC_LONG 2 #define NEC_HEADER1 3 #define NEC_HEADER2 4 void __ISR(_INPUT_CAPTURE_5_VECTOR, ipl3) _ic5(void) { mIC5ClearIntFlag(); #if NEW_NEC Uint32 rmc_tick_diff; unsigned char rmc_data_done = False; unsigned char customCode, customCode_; unsigned char dataCode, dataCode_; static unsigned char rmc_data_rx = False, nec_data = NEC_NONE; static unsigned long long rmc_tick_prev = 0, rmc_data_tick = 0; static int rmc_data_rx_step = 0, rmc_rx_data_num = 0; rmc_tick_diff = (Uint32)(g_Tick0 - rmc_tick_prev); rmc_tick_prev = g_Tick0; //64us를 하나의 tick이라 할 때 //시작을 알림 (9ms + 4.5ms) // 9ms : 140 . 625 // 4.5ms : 70 . 312 // Logic 1 (560us + 1690us) // 560us : 8.75 // 1690us : 26.40 // Logic 0 (560us + 560us) // 560us : 8.75 // 560us : 8.75 #define NEC_START1 138 #define NEC_START2 68 #define NEC_DATA_PRE 9 #define NEC_HIGH 27 #define NEC_LOW 9 if( IsNEC( rmc_tick_diff , NEC_START1 ) ) nec_data = NEC_HEADER1; else if( IsNEC( rmc_tick_diff , NEC_START2 ) ) { if(nec_data == NEC_HEADER1) {//if Header2 comes after Header1 then Initialize for getting RMC data nec_data = NEC_HEADER2; rmcRecvData = 0; rmc_rx_data_num = 0; rmc_data_rx_step = 10; } } else if( IsNEC( rmc_tick_diff , NEC_LOW ) ) nec_data = NEC_SHORT; else if( IsNEC( rmc_tick_diff , NEC_HIGH ) ) nec_data = NEC_LONG; else nec_data = NEC_NONE; switch(rmc_data_rx_step) { case 0://IDLE break; case 10://get low if(nec_data == NEC_SHORT) rmc_data_rx_step = 11; break; case 11://check data if(nec_data == NEC_SHORT) { // myprintf("0"); rmcRecvData = rmcRecvData << 1; rmcRecvData |= 0; rmc_data_rx_step =10; rmc_rx_data_num++; } else if(nec_data == NEC_LONG) { // myprintf("1"); rmcRecvData = rmcRecvData << 1; rmcRecvData |= 1; rmc_data_rx_step =10; rmc_rx_data_num++; } break; } if( rmc_rx_data_num == 32 ) { rmc_rx_data_num = 0; rmc_data_rx_step = 0; rmc_data_done = True; } if(rmc_data_done) { //myprintf(" 0x%X\r\n", rmcRecvData); customCode = (unsigned char)((rmcRecvData >> 24) & 0xFF); customCode_ = (unsigned char)((rmcRecvData >> 16) & 0xFF); dataCode = (unsigned char)((rmcRecvData >> 8) & 0xFF); dataCode_ = (unsigned char)((rmcRecvData >> 0) & 0xFF); //myprintf(" 0x%X 0x%X 0x%X 0x%X\r\n", customCode, customCode_, dataCode, dataCode_); if(customCode == 0x91 && customCode_ == 0x6F) { Uint16 rmc_data_tick_diff = (Uint16)(g_Tick2 - rmc_data_tick); //myprintf("data: %d del: %d\r\n", dataCode, rmc_data_tick_diff); // Standby에서 막 깨어났을 때 리모컨의 ON/OFF버튼이 눌려서 깨어났는지 확인 if(dataCode == RMC_ONOFF) g_WhoWakeMeUp = RMC_ONOFF; //계속 누르고 있다. if(rmc_data_tick_diff <= 300) flagRmcHold = TRUE; flagRmcTimerOn = TRUE; // 리모컨 신호가 들어왔다는 의미 // 리모컨 신호가 이제 막 들어왔다. if(flagRmcHold == FALSE) { // ON/OFF버튼이면 시간을 재기 시작한다(1.6초) if(dataCode == RMC_ONOFF) { tickRecvRmcOnOff = g_Tick1; rmcEvent = dataCode; rmcPrevData = dataCode; //PrintS("here - 2\r\n"); } else { rmcEvent = dataCode; rmcPrevData = dataCode; //PrintS("here - 3\r\n"); } rmc_btn_pressed_flag = ON; } // 리모컨이 계속 눌려서 들어오는 신호이다.(350ms 이내) else { //PrintS("here - 4\r\n"); // 300ms 이내에 다른 버튼을 눌렀다. if(rmcPrevData != dataCode) { //rmc_btn_pressed_flag = OFF; rmc_btn_pressed_flag = ON; //Led_Control(RED_OFF); flagRmcTimerOn = FALSE; flagRmcHold = FALSE; // 350ms동안 리모컨 신호가 없다는 것은 버튼을 뗐다. tickRecvRmc = 0; //rmcEvent = RMC_NONE; rmcEvent = dataCode; rmcPrevData = dataCode; //PrintS("here - 5\r\n"); } else if(dataCode == RMC_ONOFF) { //PrintS("here - 6\r\n"); // 1초간 누르고 있으면 Power Save if((g_Tick1 - tickRecvRmcOnOff) > 1785) { PrintS("here - 7\r\n"); // 이제 막 깨어난게 아니어야 한다 if(g_FlagWakeup == TRUE) { rmcEvent = RMC_POWERSAVE; flagRmcPowerSave = TRUE; PrintS("here - 8\r\n"); } rmcPrevData = dataCode; } } } rmc_btn_tick = g_Tick2; tickRecvRmc = g_Tick1; rmc_data_tick = g_Tick2; //이전에 data를 받은 시간 } } #endif #if OLD_NEC unsigned char isFail = TRUE; unsigned char customCode;//, customCode_; unsigned char dataCode, dataCode_; Uint32 temp ; int32 cmpTick[2] = {NEC_LEADER_CODE_HIGH, NEC_LEADER_CODE_LOW}; temp = (Uint32)(g_Tick0 - rmcPrevTick0); rmcPrevTick0 = g_Tick0; // myprintf("diff: %d\r\n", temp); switch(rmcRecvState) { case 0: // Leader Code 확인 // (+, -) 3tick 정도의 시간오차는 허용 (3tick = 3 * 64us = 252us) if(absI(cmpTick[rmcRecvCnt] - temp) <= 4) { rmcCount++; if(++rmcRecvCnt == 2) { rmcRecvCnt = 32; // custom & data code 합치면 모두 32비트데이터이다. rmcRecvState = 1; rmcRecvData = 0x00; // custom & data code 받을 준비 } isFail = FALSE; } break; case 1: // custom code & data code 받기 if(absI(NEC_BIT_START - temp) <= 4) { rmcCount++; rmcRecvState = 2; isFail = FALSE; } break; case 2: if(absI(NEC_BIT_HIGH - temp) <= 4) { rmcCount++; rmcRecvData |= (0x80000000 >> (rmcRecvCnt - 1)); if(--rmcRecvCnt == 0) rmcRecvState = 3; else rmcRecvState = 1; isFail = FALSE; } else if(absI(NEC_BIT_LOW - temp) <= 4) { rmcCount++; if(--rmcRecvCnt == 0) rmcRecvState = 3; else rmcRecvState = 1; isFail = FALSE; } break; default: break; } // 신호를 다 받았으면 ?왔는지 검사한다. // custom과 data 모두 반전된 값이 오므로 비교 검사한다. if(rmcRecvState == 3) { customCode = (unsigned char)(rmcRecvData & 0xFF); //customCode_ = (unsigned char)((rmcRecvData >> 8) & 0xFF); // FIXME : remocon의 custom code는 무엇인가? // (주)한울로보틱스 custom code == 137 //if((customCode == ~customCode_) && customCode == 0x89) if(customCode == 0x89) { dataCode = (unsigned char)((rmcRecvData >> 16) & 0xFF); dataCode_ = (unsigned char)((rmcRecvData >> 24) & 0xFF); dataCode_ = ~dataCode_ & 0xFF; rmcCount = 0; rmcRecvCnt = 0; rmcRecvState = 0; //SJ_DEBUG(StringInt, "RMC: ","",dataCode,0.0); // Standby에서 막 깨어났을 때 리모컨의 ON/OFF버튼이 // 눌려서 깨어났는지 확인 if(dataCode == RMC_ONOFF) g_WhoWakeMeUp = RMC_ONOFF; // 이전 신호와 지금 신호간의 시간차가 350ms이내 이면 버튼을 누르고 있다는 의미 if((g_Tick1 - tickRecvRmc) < 625) flagRmcHold = TRUE; // PrintS("tick, "); PrintI(g_Tick1); PrintS(", prev,"); PrintI(tickRecvRmc); PrintS(", diff, "); PrintS("\r\n"); tickRecvRmc = g_Tick1; flagRmcTimerOn = TRUE; // 리모컨 신호가 들어왔다는 의미 // 리모컨 신호가 이제 막 들어왔다. if(flagRmcHold == FALSE) { // PrintS("here - 1\r\n"); // ON/OFF버튼이면 시간을 재기 시작한다(1.6초) if(dataCode == RMC_ONOFF) { tickRecvRmcOnOff = g_Tick1; rmcEvent = dataCode; rmcPrevData = dataCode; // PrintS("here - 2\r\n"); } else { rmcEvent = dataCode; rmcPrevData = dataCode; // PrintS("here - 3\r\n"); } rmc_btn_pressed_flag = ON; } // 리모컨이 계속 눌려서 들어오는 신호이다.(350ms 이내) else { // PrintS("here - 4\r\n"); // 300ms 이내에 다른 버튼을 눌렀다. if(rmcPrevData != dataCode) { rmc_btn_pressed_flag = OFF; Led_Control(RED_OFF); //SJ_DEBUG(StringOnly, "off", "", 0, 0.0); flagRmcTimerOn = FALSE; flagRmcHold = FALSE; // 350ms동안 리모컨 신호가 없다는 것은 버튼을 뗐다. tickRecvRmc = 0; rmcEvent = RMC_NONE; rmcPrevData = dataCode; // PrintS("here - 5\r\n"); } else if(dataCode == RMC_ONOFF) { // PrintS("here - 6\r\n"); // 1초간 누르고 있으면 Power Save if((g_Tick1 - tickRecvRmcOnOff) > 1785) { // PrintS("here - 7\r\n"); // 이제 막 깨어난게 아니어야 한다 if(g_FlagWakeup == TRUE) { rmcEvent = RMC_POWERSAVE; flagRmcPowerSave = TRUE; // PrintS("here - 8\r\n"); } rmcPrevData = dataCode; } } } } else isFail = TRUE; } // 신호 수신중 실패하면 초기화하고 다시 받을 준비한다. if(isFail == TRUE) { rmcCount = 0; rmcRecvCnt = 0; rmcRecvState = 0; } #endif // myprintf( "rmc: %d \r\n", rmcRecvState ); }
//예제 소스 끝
'Enginius > Firmware' 카테고리의 다른 글
Atmel Studio6환경에서 JMOD 128-1에 UART 인터럽트와 IO제어 (6) | 2012.05.31 |
---|---|
VUSB (AVRUSB) usb를 avr의 gpio를 이용해서 제어. (0) | 2011.01.26 |
PIC32 - UART (2) | 2010.04.13 |
PIC32 - TIMER (0) | 2010.04.13 |
PIC32 - SPI (0) | 2010.04.13 |