전 글 적외선(IR) 신호 읽기 - ATmega8에서 만든 루틴을 조금 변형해서 리모콘의 신호를 읽는 프로그램을 만들어 보겠습니다. 603LM은 리모콘에서 송신한 신호를 반전시켜서 출력합니다. 이글에서는 603LM의 출력을 그대로 low, high로 표현합니다. 즉 원래 리모콘에서 발송한 신호가 반전된 상태로 다룹니다. 또한 프로그램을 간단히 하기 위해서 모든 리모콘 신호의 길이는 8의 배수 비트를 가진 것으로 가정하고 프로그래밍합니다. 필자가 가진 여러 개의 리모콘 중에 딱 한 개의 리모콘만이 8의 배수가 아닌 비트를 가지고 있습니다. 8의 배수가 아닌 비트를 가지더라도 처리하는 프로그램을 작성할 수는 있겠지만, 코딩이 길어지고 실행 속도가 늦어질 것 같아서 8의 배수 비트를 까지만 처리하도록 프로그래밍하겠습니다.
이전의 글은 IR 신호를 받아서 timer1의 rising edge에서 low 시간을, falling edge에서 high 시간을 각각 측정해서 각각의 queue에 저장했습니다. 본 글에서는 본격적으로 리모콘의 신호를 받아서 그 값을 구하여 문자형 LCD에 출력할 것입니다. 이 과정에서 굳이 low 시간과 high 시간을 모두 알 필요가 없고, 펄스의 길이만 가지고 있으면 처리가 가능합니다. 이런 이유로 timer1의 input capture 인터러트를 falling edge에서만 측정하여 queue에 넣도록 하겠습니다.
회로도와 동작하는 사진입니다.
REMOCON_QUEUE_HEAD와 REMOCON_QUEUE_TAIL, REMOCON_QUEUE_DATA를 모두 1 바이트로 하고, 이에 맞추어 QUEUE_SIZE도 256으로 정했습니다. 이렇게하면 queue의 head와 tail의 관리도 간단해 집니다. Queue head는 정보가 들어 온 후에 tail은 정보를 사용한 후에 각각 1씩 증가시킵니다. Ring형 queue의 경우 head와 tail이 최대값(이 경우는 255)에 도달한 다음에는 최소값(보통은 0)으로 가도록 해야합니다. Queue head와 tail을 각각 1바이트로 정하고 queue 크기를 256으로 정하면 특별한 조치를 취하지 않아도 head와 tail의 값이 255가 된 후에 1을 증가시키면 저절로 0이 되므로 관리가 간단해집니다.
리모콘 관련 내용을 따로 관리하기 위해서 Remocon8M.asm 파일을 만들어 프로젝트에 추가합니다. Remocon8M.asm에 timer1 input capture 인터럽트 서비스 루틴을 다음과 같이 수정하여 추가합니다.
.EQU QUEUE_SIZE = 256 .EQU IR_STANDBY = 0 .EQU IR_NEC_LEADER = 1 .EQU IR_SAMSUNG_LEADER = 2 .EQU IR_TC9012_LEADER = 3 .EQU IR_DATA_DONE = 4 .EQU IR_NO_MORE = 5 .DEF AL = R16 .DEF AH = R17 .DEF BL = R18 .DEF BH = R19 .DEF IR_QUEUE_TAIL = R20 .DEF IR_QUEUE_HEAD = R21 .DEF REMOCON_STATE = R24 .DSEG IR_QUEUE: .BYTE QUEUE_SIZE .CSEG __TIMER1_CAPT: PUSH AL IN AL,SREG PUSH AL PUSH BL PUSH BH IN BL,ICR1L IN BH,ICR1H LDI AL,0 OUT TCNT1H,AL OUT TCNT1L,AL PUSH ZL PUSH ZH LDI ZL,LOW(IR_QUEUE) LDI ZH,HIGH(IR_QUEUE) LDI AL,0 ADD ZL,IR_QUEUE_HEAD ADC ZH,AL ST Z+,BH ST Z,BL INC IR_QUEUE_HEAD INC IR_QUEUE_HEAD POP ZH POP ZL POP BH POP BL POP AL OUT SREG,AL POP AL RETI
Timer1의 input capture 인터럽트가 발생하면 그 때의 ICR1L과 ICR1H 레지스터의 값들을 각각 queue에 저장하고 IR_QUEUE_HEAD의 값을 2만큼 증가시킵니다.
Timer1의 compare match 1A 인터럽트 서비스 루틴을 다음과 같이 작성하여 Remocon8M.asm에 추가합니다.
__TIMER1_COMPA: PUSH AL IN AL,SREG CPSE IR_QUEUE_HEAD,IR_QUEUE_TAIL LDI REMOCON_STATE,IR_NO_MORE OUT SREG,AL POP AL RETI
Queue에 에이터가 들어 있는 상태라면 queue의 head와 tail이 같지 않습니다. 이 경우에 compare match 1A 인터럽트가 발생했다면 데이터의 입력이 끝난 것으로 판단하여 REMOCON_STATE 변수에 IR_NO_MORE 값을 넣습니다. 프로그램의 main에서 REMOCON_STATE 변수의 값을 검사하다가 IR_NO_MORE일 경우에 리모콘의 신호 값을 분석하도록 할 것입니다.
다음은 queue에 있는 데이터들을 대상으로하여 리모콘 신호를 분석하는 함수 ANALYZE_REMOCON과 GET_DATA_FROM_IR_QUEUE입니다. 역시 Remocon8M.asm에 추가합니다.
.EQU NEC_LEADER_LOW = 9000 .EQU NEC_LEADER_HIGH = 4500 .EQU NEC_LEADER_LENGTH = (NEC_LEADER_LOW + NEC_LEADER_HIGH) // 0x34BC .EQU NEC_0_LOW = 560 .EQU NEC_0_HIGH = 565 .EQU NEC_0_LENGTH = (NEC_0_LOW + NEC_0_HIGH) // 0x0465 .EQU NEC_1_LOW = 560 .EQU NEC_1_HIGH = 1690 .EQU NEC_1_LENGTH = (NEC_1_LOW + NEC_1_HIGH) // 0x08CA .EQU NEC_REPEATE_LOW = NEC_LEADER_LOW .EQU NEC_REPEATE_HIGH = 250 .EQU NEC_REPEATE_STOP_LENGTH = 300 .EQU NEC_REPEATE_LENGTH = (NEC_REPEATE_LOW + NEC_REPEATE_HIGH + NEC_REPEATE_STOP_LENGTH) // 0x2D1E .EQU NEC_MIN_LEADER_LENGTH = (NEC_LEADER_LENGTH - 1000) // 0x30D4 12,500 .EQU NEC_MAX_LEADER_LENGTH = (NEC_LEADER_LENGTH + 700) // 0x3778 14,200 .EQU NEC_MIN_0_LENGTH = (NEC_0_LENGTH - 64) // 0x0425 1,061 .EQU NEC_MAX_0_LENGTH = (NEC_0_LENGTH + 64) // 0x04A5 1,189 .EQU NEC_MIN_1_LENGTH = (NEC_1_LENGTH - 160) // 0x082A 2,090 .EQU NEC_MAX_1_LENGTH = (NEC_1_LENGTH + 160) // 0x096A 2,410 .EQU NEC_MIN_REPEATE_LENGTH = (NEC_REPEATE_LENGTH - 64) // 0x2CDE .EQU NEC_MAX_REPEATE_LENGTH = (NEC_REPEATE_LENGTH + 64) // 0x2D5E .EQU SAMSUNG_LEADER_LOW = 3000 .EQU SAMSUNG_LEADER_HIGH = 8500 .EQU SAMSUNG_LEADER_LENGTH = (SAMSUNG_LEADER_LOW + SAMSUNG_LEADER_HIGH) // 0x2CEC 11,500 .EQU SAMSUNG_0_LOW = 550 .EQU SAMSUNG_0_HIGH = 520 .EQU SAMSUNG_0_LENGTH = (SAMSUNG_0_LOW + SAMSUNG_0_HIGH) // 0x042E 1,070 .EQU SAMSUNG_1_LOW = 550 .EQU SAMSUNG_1_HIGH = 1550 .EQU SAMSUNG_1_LENGTH = (SAMSUNG_1_LOW + SAMSUNG_1_HIGH) // 0x0834 2,100 .EQU SAMSUNG_MIN_LEADER_LENGTH = (SAMSUNG_LEADER_LENGTH - 1000) // 0x2904 10,500 .EQU SAMSUNG_MAX_LEADER_LENGTH = (SAMSUNG_LEADER_LENGTH + 1000) // 0x30D4 12,500 .EQU SAMSUNG_MIN_0_LENGTH = (SAMSUNG_0_LENGTH - 80) // 0x03DE 990 .EQU SAMSUNG_MAX_0_LENGTH = (SAMSUNG_0_LENGTH + 80) // 0x047E 1,150 .EQU SAMSUNG_MIN_1_LENGTH = (SAMSUNG_1_LENGTH - 80) // 0x07E4 2,020 .EQU SAMSUNG_MAX_1_LENGTH = (SAMSUNG_1_LENGTH + 80) // 0x0884 2,180 .EQU TC9012_LEADER_LOW_LENGTH = 4500 .EQU TC9012_LEADER_HIGH_LENGTH = 4500 .EQU TC9012_LEADER_LENGTH = (TC9012_LEADER_LOW_LENGTH + TC9012_LEADER_HIGH_LENGTH) //0x2328 9000 .EQU TC9012_MIN_LEADER_LENGTH = (TC9012_LEADER_LENGTH - 100) //0x22C4 8900 .EQU TC9012_MAX_LEADER_LENGTH = (TC9012_LEADER_LENGTH + 410) //0x24C2 9410 .DEF REMOCON_BYTES = R14 .DEF REMOCON_CURRENT_BITS = R15 .DEF REMOCON_DATA = R23 .DSEG REMOCON_CODE: .BYTE 32 .CSEG ANALYZE_REMOCON: CLI CLR REMOCON_CURRENT_BITS CLR REMOCON_DATA CLR REMOCON_BYTES LDI ZL,LOW(IR_QUEUE) LDI ZH,HIGH(IR_QUEUE) LDI AL,0 ADD ZL,IR_QUEUE_TAIL ADC ZH,AL ANALYZE_REMOCON_SEARCH_LEADER: CP IR_QUEUE_TAIL,IR_QUEUE_HEAD BREQ ANALYZE_REMOCON_QUIT // if no more data in queue, return RCALL GET_DATA_FROM_IR_QUEUE LDI YL,LOW(TC9012_MIN_LEADER_LENGTH) LDI YH,HIGH(TC9012_MIN_LEADER_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_SEARCH_LEADER // if shorter than MIN_TC9012_LEADER_LENGTH, can't be any LEADER LDI YL,LOW(TC9012_MAX_LEADER_LENGTH) LDI YH,HIGH(TC9012_MAX_LEADER_LENGTH) CP YL,BL CPC YH,BH BRCS ANALYZE_REMOCON_SEARCH_LEADER_SAMSUNG // if longer than MAX_TC9012_LEADER_LENGTH. check if SAMSUNG_LEADER LDI REMOCON_STATE,IR_TC9012_LEADER // TC9012 leader is found RJMP ANALYZE_REMOCON_DATA ANALYZE_REMOCON_SEARCH_LEADER_SAMSUNG: LDI YL,LOW(SAMSUNG_MIN_LEADER_LENGTH) LDI YH,HIGH(SAMSUNG_MIN_LEADER_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_SEARCH_LEADER // if shorter than MIN_SAMSUNG_LEADER_LENGTH, can't be a LEADER LDI YL,LOW(SAMSUNG_MAX_LEADER_LENGTH) LDI YH,HIGH(SAMSUNG_MAX_LEADER_LENGTH) CP YL,BL CPC YH,BH BRCS ANALYZE_REMOCON_SEARCH_LEADER_NEC // if longer than MAX_SAMSUNG_LEADER_LENGTH. check if NEC_LEADER LDI REMOCON_STATE,IR_SAMSUNG_LEADER // SAMSUNG leader is found RJMP ANALYZE_REMOCON_DATA_SAMSUNG ANALYZE_REMOCON_SEARCH_LEADER_NEC: LDI YL,LOW(NEC_MIN_LEADER_LENGTH) LDI YH,HIGH(NEC_MIN_LEADER_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_SEARCH_LEADER // if shorter than MIN_NEC_LEADER_LENGTH, can't be a LEADER LDI YL,LOW(NEC_MAX_LEADER_LENGTH) LDI YH,HIGH(NEC_MAX_LEADER_LENGTH) CP YL,BL CPC YH,BH BRCS ANALYZE_REMOCON_SEARCH_LEADER // if longer than MAX_NEC_LEADER_LENGTH, can't be a LEADER LDI REMOCON_STATE,IR_NEC_LEADER // NEC leader is found RJMP ANALYZE_REMOCON_DATA ANALYZE_REMOCON_QUIT: SEI RET ANALYZE_REMOCON_DATA: CP IR_QUEUE_TAIL,IR_QUEUE_HEAD BREQ ANALYZE_REMOCON_QUIT RCALL GET_DATA_FROM_IR_QUEUE LDI YL,LOW(NEC_MIN_0_LENGTH) LDI YH,HIGH(NEC_MIN_0_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_QUIT // shorter than low MIN length LDI YL,LOW(NEC_MAX_0_LENGTH) LDI YH,HIGH(NEC_MAX_0_LENGTH) CP YL,BL CPC YH,BH BRCC ANALYZE_REMOCON_DATA_0 // data 0 LDI YL,LOW(NEC_MIN_1_LENGTH) LDI YH,HIGH(NEC_MIN_1_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_QUIT // shorter than high MIN length LDI YL,LOW(NEC_MAX_1_LENGTH) LDI YH,HIGH(NEC_MAX_1_LENGTH) CP YL,BL CPC YH,BH BRCS ANALYZE_REMOCON_QUIT // longer than high MAX length // data 1 ANALYZE_REMOCON_DATA_1: SBR REMOCON_DATA,0x80 RJMP ANALYZE_REMOCON_DATA_SIGNAL // data 0 ANALYZE_REMOCON_DATA_0: CBR REMOCON_DATA,0x80 ANALYZE_REMOCON_DATA_SIGNAL: MOV AL,REMOCON_CURRENT_BITS INC REMOCON_CURRENT_BITS ANDI AL,0x07 CPI AL,0x07 BRNE ANALYZE_REMOCON_DATA_SIGNAL_SHIFT LDI YL,LOW(REMOCON_CODE) LDI YH,HIGH(REMOCON_CODE) CLR AL ADD YL,REMOCON_BYTES ADC YH,AL ST Y,REMOCON_DATA INC REMOCON_BYTES CLR REMOCON_DATA CPI REMOCON_STATE,IR_SAMSUNG_LEADER BREQ ANALYZE_REMOCON_DATA_SAMSUNG RJMP ANALYZE_REMOCON_DATA ANALYZE_REMOCON_DATA_SIGNAL_SHIFT: LSR REMOCON_DATA CPI REMOCON_STATE,IR_SAMSUNG_LEADER BREQ ANALYZE_REMOCON_DATA_SAMSUNG RJMP ANALYZE_REMOCON_DATA ANALYZE_REMOCON_DATA_SAMSUNG: CP IR_QUEUE_TAIL,IR_QUEUE_HEAD BREQ ANALYZE_REMOCON_QUIT RCALL GET_DATA_FROM_IR_QUEUE LDI YL,LOW(SAMSUNG_MIN_0_LENGTH) LDI YH,HIGH(SAMSUNG_MIN_0_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_QUIT_SUB2 // shorter than SAMSUNG low MIN length LDI YL,LOW(SAMSUNG_MAX_0_LENGTH) LDI YH,HIGH(SAMSUNG_MAX_0_LENGTH) CP YL,BL CPC YH,BH BRCC ANALYZE_REMOCON_DATA_0 // data 0 LDI YL,LOW(SAMSUNG_MIN_1_LENGTH) LDI YH,HIGH(SAMSUNG_MIN_1_LENGTH) CP BL,YL CPC BH,YH BRCS ANALYZE_REMOCON_QUIT_SUB2 // shorter than SAMSUNG high MIN length LDI YL,LOW(SAMSUNG_MAX_1_LENGTH) LDI YH,HIGH(SAMSUNG_MAX_1_LENGTH) CP YL,BL CPC YH,BH BRCC ANALYZE_REMOCON_DATA_1 // data 1 ANALYZE_REMOCON_QUIT_SUB2: DEC IR_QUEUE_TAIL // to restore SAMSUNG leader signal DEC IR_QUEUE_TAIL RJMP ANALYZE_REMOCON_QUIT ////////////////////////////////////////////////// // GET_DATA_FROM_IR_QUEUE: // PARAM NONE // RETURN BH:BL // CHANGED Z ////////////////////////////////////////////////// GET_DATA_FROM_IR_QUEUE: LD BH,Z+ LD BL,Z+ INC IR_QUEUE_TAIL INC IR_QUEUE_TAIL BRNE GET_DATA_FROM_IR_QUEUE_QUIT LDI ZL,LOW(IR_QUEUE) LDI ZH,HIGH(IR_QUEUE) GET_DATA_FROM_IR_QUEUE_QUIT: RET
소스프로그램 루틴이 상당히 길지만 차근차근 살펴 보겠습니다.
필자가 가지고 있는 몇 가지 리모콘들을 분석해 보니 3 종류가 있었습니다.
첫째는 NEC 프로토콜의 리모콘
둘째는 TC9012 프로토콜을 사용하는 리모콘(ex:삼성 TV 리모콘)
셋째는 무슨 프로토콜인지 모르겠으나 삼성 하우젠 천정형 에어콘 리모콘(모델명 ARC-4AC)
(이하 소스프로그램과 설명에서 SAMSUNG_을 접두어로 붙인 것는 이 삼성 하우젠 천정형 에어콘 리모콘을 의미합니다.)
NEC 프로토콜과 TC9012 프로토콜을 사용하는 대부분의 리모콘들은 4바이트의 신호를 보내옵니다. 처음의 두 바이트는 제조사의 커스텀 신호이고 세번째 바이트가 명령 신호이며, 네번째 바이트는 세번째 바이트의 2의 보수입니다. 그런데 삼성 하우젠 천정형 리모콘의 경우는 LEADER의 신호 길이와 데이터 비트의 신호 길이도 다르지만 보내 오는 데이터도 다릅니다. 기본적으로 56비트(7바이트)의 신호를 보내옵니다. 같은 전원 버튼을 눌러도 전원을 켤 때는 7바이트의 신호 하나를 보내오지만, 전원을 끌 때에는 7바이트 신호를 연속해서 두 개 보내옵니다.
리모콘 신호를 분석하는 알고리즘을 개략적으로 설명하면 다음과 같습니다.
① LEADER 신호를기다립니다. LEADER 신호를 찾기 전에 들어 오는 신호는 모두 오류 신호로 간주하여 버립니다.
② LEADER 신호를 찾으면 이후의 데이터들을 분석합니다. 각 LEADER 신호의 프로토콜에 맞는 0과 1의 길이에 따라 데이터를 처리합니다. 만약 0도아니고 1도 아닌 신호가 들어오면 데이터가 끝난 것으로 간주하고 함수 실행을 종료합니다.
위의 개략적인 순서에 따라 위 소스 프로그램을 분석해 보겠습니다. 1행부터 43행까지는 각각의 리모콘에서 발생하는 신호들의 길이를 매크로로 정의한 것입니다. NEC로 시작하는 매크로들은 NEC 프로토콜을 사용하는 리모콘들의 값을, TC9012로 시작하는 매크로들은 TC9012프로토콜을 사용하는 리모콘들의 값을, SAMSUNG으로 시작하는 매크로들은 삼성 에어콘 리모콘들의 값을 정의한 것들입니다.
다음 설명 중에서 * 기호는 NEC와 TC9012, SAMSUNG을 모두 통칭하는 의미로 사용합니다. 매크로들 중에서 *_LEADER_LOW는 리더 신호중 LOW(0)인 시간의 길이를, *_LEADER_HIGH는 리더 신호 중 HIGH(1)인 시간의 길이를 정의한 것입니다. *_LEADER_LENGTH는 *_LEADER_LOW와 *_LEADER_HIGH를 합한 값입니다. 마찬가지로 *_0_LOW는 비트 값이 0인 신호의 LOW(0) 신호 길이를, *_0_HIGH는 비트 값이 0인 신호의 HIGH(1)인 신호 길이를 정의하였고, *_0_LENGTH는 *_0_LOW와 *_0_HIGH를 합한 값입니다.
매크로 NEC_LEADER_LENGTH는 13,500의 값을, NEC_0_LENGTH는 1,125의 값을, NEC_1_LENGTH는 2,250의 값을 갖습니다. 그런데, 실제로 NEC 프로토콜을 사용하는 리모콘 신호들을 검사해 보면 리모콘마다 신호의 길이가 조금씩 다릅니다. 앞의 글 적외선(IR) 신호 읽기 - ATmega8"에서 만든 프로그램으로 여러 개의 리모콘 신호들을 분석해서 임의로 최소값과 최대값을 정했습니다. 매크로를 최소값에는 _MIN_을 최대값에는 _MAX_를 추가해서 매크로를 만들었습니다. NEC 프로토콜의 경우 NEC_MIN_LEADER_LENGTH보다 크거나 같고, NEC_MAX_LEADER_LENGTH보다 작거나 같으면 NEC 프로토콜의 LEADER 신호가 온 것으로 여깁니다. 마찬가지로 NEC_MIN_0_LENGTH보다 크거나 같고 NEC_MAX_0_LENGTH보다 작거나 같으면 신호 0인 것으로, NEC_MIN_1_LENGTH보다 크거나 같고 NEC_MAX_1_LENGTH보다 작거나 같으면 신호 1인 것으로판단합니다.
본격적으로 ANALYZE_REMOCON 함수를 분석해 보겠습니다.
① IR 신호를 분석하는 중에 신호가 들어오면 혼란이 생길 수 있으므로 일단 인터럽트를 중지시킵니다(54행).
② 리모콘 관련 변수들인 REMOCON_CURRENT_BITS, REMOCON_DATA, REMOCON_BYTES 변수의 값을 0으로 설정합니다(55행 ~ 57행). REMOCON_CURRENT_BITS에는 현재까지 파악한 리모콘 신호의 비트 수를 담고, REMOCON_DATA는 현재 처리 중인 리모콘 코드를 가지도록 합니다. REMOCON_BYTES에는 현재까지 들어온 리모콘 신호가 몇 바이트인지를 저장합니다. main에서 ANALYZE_REMOCON을 호출한 후에 REMOCON_BYTES가 0이면 들어온 신호가 없는 것으로 간주합니다.
③ Z 레지스터에 IR_QUEUE의 주소를 담고, 가져올 데이터가 있는 주소를 가리키도록 IR_QUEUE_TAIL의 값을 더합니다.(58행 ~ 62행).
④ LEADER 신호가 있는지 찾습니다(63행 ~ 104행).
i) 데이터가 끝나도록 LEADER 신호를 찾지 못하면 ANALYZE_REMOCON_QUIT로 가서, 인터럽트를 활성화하고 루틴을 종료합니다(64, 65행).
ii) 데이터가 더 있다면 GET_DATA_FROM_IR_QUEUE 함수를 호출하여 IR_QUEUE에 있는 데이터 2 바이트를 BH:BL에 담아 옵니다(66행).
iii) 이 데이터를 TC9012_MIN_LEADER_LENGTH와 비교하여 크거나 같으면 TC9012_MAX_LEADER_LENGTH와 비교하러 가고, 작으면 이 값보다 작은 LEADER 신호는 없으므로 다음 데이터를 검사하기 위해 ANALYZE_REMOCON_SEARCH_LEADER로 되돌아 갑니다(71행).
iv) 위 iii)단계를 통과했으면 TC9012_MAX_LEADER_LENGTH와 비교하여, 크면 TC9012의 LEADER 신호가 아니므로 SAMSUNG_LEADER인지 확인하러 갑니다(76행).
v) TC9012_MAX_LEADER_LENGTH보다 작거나 같으면, TC9012 LEADER 신호이므로 REMOCON_STATE 변수에 IR_TC9012_LEADER를 넣고 ANALYZE_REMOCON_DATA로 갑니다(77, 78행).
vi) 위의 TC9012 프로토콜의 LEADER 신호를 찾는 과정과 유사한 과정을 거쳐서 SAMSUNG의 LEADER 신호와(79행 ~ 91행) NEC 프로토콜의 LEADER 신호(92행 ~ 104행)를 찾습니다. SAMSUNG LEADER 신호를 찾았으면 REMOCON_STATE 변수에 IR_SAMSUNG_LEADER를 저장하고 ANALYZE_REMOCON_SAMSUNG_DATA로 갑니다(90, 91행). NEC 프로토콜의 LEADER 신호를 찾았으면 REMOCON_STATE 변수에 IR_NEC_LEADER 값을 저장한 후에 ANALYZE_REMOCON_DATA로 갑니다(103, 104행).
vii) 아무런 LEADER 신호도 찾지 못했을 때에는 다음 데이터를 검사하기 위해 ANALYZE_REMOCON_SEARCH_LEADER로 되돌아 갑니다(102행).
⑤ 위의 ④단계에서 LEADER 신호를 찾은 경우 TC9012 프로토콜과 NEC 프로토콜의 경우는 ANALYZE_REMOCON_DATA로, SAMSUNG의 경우는 ANALYZE_REMOCON_DATA_SAMSUNG으로 분기하여 신호를 해석합니다(109행 ~ 190행). ANALYZE_REMOCON_DATA 루틴과 ANALYZE_REMOCON_DATA_SAMSUNG 루틴은 알고리즘은 같으나 0과 1의 신호 길이가 달라서 따로 작성했습니다. 즉 TC9012 프로토콜의 신호 0과 1의 길이와 NEC 프로토콜의 신호 0과 1의 길이는 스펙 상으로 같기때문에 같은 루틴을 사용합니다. 반면에 SAMSUNG 하우젠 천정형 에어콘 리모콘의 신호 길이는 NEC 프로토콜 신호의 길이보다는 조금 짧은 듯한데, 정확한 값을 모르겠어서 대충 어림짐작으로 정했습니다.
i) 데이터가 더 없으면 함수를 마치고 리턴합니다(TC9012와 NEC 프로토콜의 경우 110, 111행, SAMSUNG의 경우 164, 165행).
ii) 데이터가 더 있으면 GET_DATA_FROM_IR_QUEUE 함수를 호출해서 데이터를 BH:BL에 담아 옵니다(TC9012와 NEC 프로토콜의 경우 112행, SAMSUNG의 경우 166행).
iii) 신호의 길이가 MIN_0_LENGTH와 크거나 같고 MAX_0_LENGTH보다 작거나 같으면, 신호 0이 들어 온 것이므로 ANALYZE_REMOCON_DATA_0으로 분기합니다(TC9012와 NEC 프로토콜의 경우 113행 ~ 122행, SAMSUNG의 경우 167행 ~ 176행).
iv) 신호의 길이가 MIIN_1_LENGTH보다 크거나 같고 MAX_1_LENGTH보다 작거나 같으면, 신호 1이 들어 온 것이므로 ANALYZE_REMOCON_DATA_1로 분기합니다(TC9012와 NEC 프로토콜의 경우 123행 ~ 132행, SAMSUNG의 경우 177행 ~ 186행).
v) 신호의 길이가 0에도 해당되지 않고 1에도 해당하지 않으면, 오류 신호이므로 함수를 종료하고 나갑니다(TC9012와 NEC 프로토콜의 경우 117행과 132행, SAMSUNG의 경우 181행과 186행). 다만 SAMSUNG 하우젠 천정형 에어콘 리모콘의 경우 첫 데이터에 바로 이어서 다음 신호의 LEADER가 들어 오므로 다음 번 ANALYZE_REMOCON 함수를 호출할 때에 LEADER 신호를 잃어버리지 않도록 하기 위해서 IR_QUEUE_TAIL의 값을 2만큼 줄인 후에 함수를 종료합니다(188행 ~ 190행).
⑥ 위의 ⑤단계에서 신호가 1로 판정되었으면 ANALYZE_REMOCON_DATA_1로, 0으로 판정되었으면 ANALYZE_REMOCON_DATA_0으로 분기합니다.
i) 판정된 신호에 따라 REMOCON_DATA 변수의 최상위 비트를 1 또는 0으로 설정(135행, 139행)합니다.
ii) REMOCON_CURRENT_BITS의 값을 AL로 복사한(141행) 후에 REMOCON_CURRENT_BITS를 1 증가시킵니다(142행).
iii) AL의 값을 검사하여 현재 데이터가 8의 배수 번째 데이터이면, REMOCON_DATA 변수의 내용을 REMOCON_CODE의 REMOCON_BYTE 번째 위치에 저장(146행 ~ 151행), REMOCON_BYTES 1증가(152행,) REMOCON_DATA의 값을 0으로 초기화(153행) 등의 작업을 합니다. 이후에 REMOCON_STATE 변수에 저장된 값을 참조하여 원래 루틴으로 복귀합니다(155, 156행).
iv) 현재 데이터가 8의 배수 번째 데이터가 아니면, 다음 데이터를 받기 위해서 REMOCON_DATA 변수의 값을 오른쪽으로 1비트씩 이동 시킵니다(158행). 이후에 REMOCON_STATE 변수의 값을 참조하여 원래의 루틴으로 복귀합니다(159행 ~ 161행).
v) 위의 iii), iv)단계에서 원래의 루틴으로 복귀한 다음에는 다시 ⑤의 i)단계부터 반복합니다.
GET_DATA_FROM_IR_QUEUE 함수는 IR_QUEUE에서 레지스터 Z가 가리키는 위치의 데이터 2바이트를 BH와 BL 레지스터에 담아 리턴합니다. 이 때에 다음 데이터를 위해서 IR_QUEUE_TAIL의 값을 2만큼 증가시키는데(201, 202행), IR_QUEUE_TAIL 증가시 IR_QUEUE_TAIL 변수의 값이 255를 넘는 경우에는 레지스터 Z가 IR_QUEUE의 처음을 가리키도록 조치합니다(204, 205행).
다음은 Timer1의 Input caputre 인터럽트와 Output compare 1A 인터럽트가 발생하도록 Timer1을 설정하는 함수입니다. 이 함수도 Remocon8M.asm 파일에 추가합니다.
.EQU TCCR1B_IRIN_VALUE = (1 << CS11) // falling edge, prescale 1/8 .EQU TIMSK_IRIN_VALUE = ((1 << TICIE1) | (1 << OCIE1A)) .EQU IR_DATA_TIMEOUT = 30000 // mcro second ////////////////////////////////////////////////// // TIMER1_IRIN: // PARAM NONE // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// TIMER1_IRIN: LDI AL,TCCR1B_IRIN_VALUE OUT TCCR1B,AL LDI AL,TIMSK_IRIN_VALUE OUT TIMSK,AL LDI AL,0 OUT TCNT1H,AL OUT TCNT1L,AL LDI AL,HIGH(IR_DATA_TIMEOUT) OUT OCR1AH,AL LDI AL,LOW(IR_DATA_TIMEOUT) OUT OCR1AL,AL RET
다음은 위 ANALYZE_REMOCON 함수를 사용하는 메인 루틴입니다. 이 루틴들은 첨부하는 파일 ReceiveIR_8M.asm에 넣습니다.
MAIN: LDI AL,LOW(RAMEND) OUT SPL,AL LDI AL,HIGH(RAMEND) OUT SPH,AL LDI AL,LCD_DATA_DDR_VALUE OUT LCD_DATA_DDR,AL LDI AL,LCD_CTRL_DDR_VALUE OUT LCD_CTRL_DDR,AL CLR AL OUT LCD_CTRL_PORT,AL OUT LCD_DATA_PORT,AL RCALL VARIABLES_INIT RCALL LCD_INIT RCALL TIMER1_IRIN LDI ZL,LOW(COPYRIGHT) LDI ZH,HIGH(COPYRIGHT) RCALL LCD_PUT_STRING_LPM LDI AL,0 LDI AH,1 RCALL LCD_GOTOXY LDI ZL,LOW(COPYDATE) LDI ZH,HIGH(COPYDATE) RCALL LCD_PUT_STRING_LPM SEI MAIN_LOOP: CPI REMOCON_STATE,IR_NO_MORE BRNE MAIN_LOOP CLR LCD_LINE MAIN_LOOP_LOOP: CPSE IR_QUEUE_HEAD,IR_QUEUE_TAIL RJMP MAIN_LOOP_DO LDI REMOCON_STATE,IR_STANDBY RJMP MAIN_LOOP MAIN_LOOP_DO: RCALL ANALYZE_REMOCON TST REMOCON_BYTES BREQ MAIN_LOOP_LOOP TST LCD_LINE BRNE MAIN_LOOP_DO_LINE2 RCALL LCD_CLEAR_DISPLAY INC LCD_LINE RJMP MAIN_LOOP_DO_DISPLAY MAIN_LOOP_DO_LINE2: LDI AL,0 LDI AH,1 RCALL LCD_GOTOXY MAIN_LOOP_DO_DISPLAY: LDI ZL,LOW(REMOCON_CODE) LDI ZH,HIGH(REMOCON_CODE) MAIN_LOOP_DISPLAY_LOOP: TST REMOCON_BYTES BREQ MAIN_LOOP_DISPLAY_LOOP_QUIT LD AL,Z+ RCALL DISPLAY_AL MAIN_DISPLAY_CONTINUE: DEC REMOCON_BYTES RJMP MAIN_LOOP_DISPLAY_LOOP MAIN_LOOP_DISPLAY_LOOP_QUIT: RJMP MAIN_LOOP_LOOP
I) 데이터가 다 들어오기를 기다립니다(29, 30행).
ii) IR_QUEUE에 데이터가 더 이상 없으면 REMOCON_STATE 변수에 IR_STANDBY 값을 넣고 데이터가 들어오기를 기다립니다.
iii) IR_QUEUE에 데이터가 남아 있으면 ANALYZE_REMOCON 함수를 호출합니다(39행).iv) REMOCON_BYTES의 값이 0이 아니면 LCD에 리모콘 신호 값을 표시합니다(42행 ~ 61행).
v) 다시 ii)단계부터 반복합니다.
삼성 하우젠 천장형 리모콘처럼 버튼을 한 번 눌렀을 때에 두 개의 신호가 들어오는 경우에 LCD의 행을 바꾸어 출력하느라고 출력 부분이 조금 복잡해졌습니다.
잘 동작하는 소스 프로그램들을 압축하여 첨부합니다.
'AVR > 작품' 카테고리의 다른 글
적외선(IR) 프로그램 수정 (0) | 2019.11.14 |
---|---|
적외선 리모콘(IR Remocon) 신호 보내기 - ATmega8 (0) | 2019.11.02 |
적외선(IR) 신호 읽기 - ATmega8 (0) | 2019.10.22 |
Click, Double Click, Long Click 구분하기 (0) | 2019.09.07 |
KCC426V와 ATtiny2313을 이용한 FM radio 만들기(3) (0) | 2019.06.24 |