이글의 전부 또는 일부, 사진, 소스프로그램 등은 저작자의 동의 없이는 상업적인 사용을 금지합니다. 또한, 비상업적인 목적이라 하더라도 출처를 밝히지 않고 게시하는 것은 금지합니다.



전 글 적외선(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의 행을 바꾸어 출력하느라고 출력 부분이 조금 복잡해졌습니다.

잘 동작하는 소스 프로그램들을 압축하여 첨부합니다.

ReceiveIR_M8.zip
5.8 kB

 

블로그 이미지

엠쿠스

Microprocessor(STM32, AVR)로 무엇인가를 만들어 보고자 학습 중입니다.

,