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

전 글에 이어서 KCC426V를 제어하는 프로그램을 계속하여 작성합니다.


1. 엔코더로 수신 주파수 바꾸기

KCC426V와 ATtiny2313을 이용한 FM radio 만들기(1)에 올린 사진을 보면 청취 주파수를 바꾸기 위해서 엔코더를 달아놨습니다. 엔코더를 시계 방향으로 돌리면 주파수를 올리고, 반시계 방향으로 돌리면 주파수를 내리도록 프로그래밍하겠습니다.

앞의 글(1)편에 올려 놓은 회로도를 보면, 엔코더의 두 출력(A, B)를 각각 1k옴 저항으로 풀업해서 ATtiny2313의 19번핀(PB6)과, 18번핀(PB5)에 연결했습니다. 이렇게 연결해 놓으면 평상시에 PB6과 PB5는 high 상태로 있습니다. 만약 엔코더를 시계 방향으로 돌리면 A, B의 값은 다음 표와 같은 순서로 변합니다.

순서 A(PB6) B(PB6)
1 1 1
2 1 0
3 0 0
4 0 1
5 1 1

 

엔코더를 반시계 방향으로 돌리면 A, B의 값은 다음 표와 같은 순서로 변합니다.

순서 A(PB6) B(PB6)
1 1 1
2 0 1
3 0 0
4 1 0
5 1 1


엔코더 값이 변하는지 여부는 외부 인터럽트로 받아 처리하는 것이 원칙이지만, 본 작품에서는 INT0, INT1 단자를 이미 LCD 제어에 할당했고 MCU가 다른 할 일도 별로 없어서 폴링 방식으로 처리하겠습니다. 엔코더 값의 변화를 처리하는 방법은 여러 가지가 있을 수 있습니다. 본 프로그램에서는 다음과 같은 방법으로 엔코더가 어느 방향으로 회전했는지를 파악합니다. PB6과 PB5의 값이 10 또는 01일 경우에 이전의 PB6과 PB5의 값이 11이었는지를 검사해서, 이전의 PB6과 PB값이 11이면 엔코더 값이 변한 것으로 처리하고, 11이 아니면 들어 온 값을 무시합니다. 변한 PB6과 PB5의 값이 10이면 시계 바향으로, 01이면 반시계 방향으로 회전한 것으로 간주합니다.

 

.EQU	ENCODER_A_BIT		= PB6
.EQU	ENCODER_B_BIT		= PB7
.EQU	CLOCKWISE		= (1 << ENCODER_B_BIT)
.EQU	COUNTER_CLOCKWISE	= (1 << ENCODER_A_BIT)
.EQU	ENCODER_AB_MSK		= (CLOCKWISE | COUNTER_CLOCKWISE)
.DSEG
	PRE_AB_VALUE		.DB	0

.CSEG
CHECK_ENCODER_AB:
	IN	AL,KCC426V_PIN
	ANDI	AL,ENCODER_AB_MSK
	PUSH	AH
	LDS	AH,PRE_AB_VALUE						// AH previous value, AL current value
	CP	AL,AH
	BREQ	CHECK_ENCODER_AB_NO_CHANGE		// encoder value is not changed
CHECK_ENCODER_AB_CHANGED:
	RCALL	WAIT_FOR_CHATTERING
	STS	PRE_AB_VALUE,AL						// store current value as previous value
	CPI	AH,ENCODER_AB_MSK
	BREQ	CHECK_ENCODER_AB_QUIT			// if previous value is ENCODER_AB_MSK(11) return AL
CHECK_ENCODER_AB_NO_CHANGE:
	LDI	AL,ENCODER_AB_MSK					// else return ENCODER_AB_MSK
CHECK_ENCODER_AB_QUIT:
	POP	AH
	RET

위의 CHECK_ENCODER_AB 함수에 간단히 주석을 달았습니다만, 간단하게 설명하면 다음과 같습니다.
1) 현재 엔코더의 값이 이전의 엔코더 값과 같으면 변화가 없다는 의미로 ENCODER_AB_MSK(11)를 리턴합니다.
2) 현재 엔코더 값이 이전 엔코더 값과 같지 않으면 현재 값을 이전 값으로 저장합니다.
3) 이전의 엔코더 값이 ENCODER_AB_MSK이면 현재 엔코더 값을 리턴합니다.
4) 이전의 엔코더 값이 ENCODER_AB_MSK가 아니면 ENCODER_AB_MSK를 리턴합니다.

위 함수를 호출한 곳에서는 AL 레지스터 값에 따라 적절한 조치를 취하면 됩니다. AL 레지스터에 CLOCKWISE(10)이 있으면 엔코더가 오른쪽으로 회전한 것으로, COUNTER_CLOCKWISE(01)이 있으면 왼쪽으로 회전한 것으로, ENCODER_AB_MSK(11)의 값이 있으면 회전하지 않은 것으로 처리하면 됩니다.

 

.EQU	FREQUENCE_STEP		= 2
.CSEG
	RCALL	CHECK_ENCODER_AB
	CPI	AL,CLOCKWISE
	BRNE	MAIN_LOOP_CNT_CLOCKWISE
	LDI	BL,LOW(FREQUENCE_STEP)		// if CLOSKWISE
	LDI	BH,HIGH(FREQUENCE_STEP)
	RJMP	MAIN_LOOP_DISPLAY_FR
MAIN_LOOP_CNT_CLOCKWISE:
	CPI	AL,COUNTER_CLOCKWISE
	BRNE	MAIN_LOOP_RJMP
	LDI	BL,LOW(-FREQUENCE_STEP)		// if COUNTER_CLOCKWISE
	LDI	BH,HIGH(-FREQUENCE_STEP)
MAIN_LOOP_DISPLAY_FR:
	LDS	AL,CURRENT_FR
	LDS	AH,CURRENT_FR + 1
	ADD	AL,BL
	ADC	AH,BH
	RCALL	DISPLAY_FREQUENCE		// send to KCC426V and display on LCD, store value to CURRENT_FR


위 코드는 앞에서 작성한 CHECK_ENCODER_AB 함수를 사용하는 예입니다. AL 레지스터에 담겨 있는 리턴값이 CLOCKWISE이면 FREQUENCE_STEP만큼 주파수 값을 올리고, COUNTER_CLOCKWISE이면 FREQUENCE_STEP만큼 주파수 값을 내립니다. FREQUENCE_STEP은 매크로로 2의 값을 갖도록 정의했습니다. 앞의 글 KCC426V와 ATtiny2313으로 FM radio 만들기(2)에서 살펴본 공식에 따르면 주파수 값이 1만큼 변하면 수신 주파수는 50KHz 바뀝니다. 따라서 엔코더를 좌우로 회전시키면 한 단계마다 0.1MHz(100KHz)씩 수신 주파수가 바뀝니다.


2. 방송 수신 상태 LCD에 표시하기



KCC426V 모듈 내에 있는 LA1827의 8번핀은 평상시에는 high 상태에 있다가 방송 주파수를 잘 잡으면 low 상태가 됩니다. 앞의 글(1)편에 올려 놓은 회로도 상의 J4의 1번핀이 LA1827의 8번핀과 연결되어 있습니다. 이 핀이 low가 되면, Vcc와 1k옴으로 회로도 상의 LED(D2)가 점등되면서 ATtiny2313의 15번핀 PB3도 low가 됩니다. 본 프로그램에서는 PB3가 low로 되는지를 감시하고 있다가 low가 아니면 빈 칸으로, low이면 "Tune"이라는 메시지를 LCD에 출력하도록 하겠습니다.

LA1827의 9번핀도 평상시에는 high로 있다가 스테레오 신호를 감지하면 low로 바뀝니다. 이 핀은 회로도 상의 J4의 2번핀과 연결되어 있습니다. 이핀이 low 상태가 되면 회로도 상의 LED(D3)가 점등되면서 ATtiny2313의 16번핀 PB4도 low 상태가 됩니다. PB4 상태를 감시하고 있다가 low가 아니면 빈 칸을, low이면 "ST"를 LCD에 출력하도록 합니다.

 

.EQU	STEREO_XPOS		= 10
.EQU	STEREO_YPOS		= 0
.EQU	TUNED_XPOS		= 13
.EQU	TUNED_YPOS		= 0

.DSEG
	PRE_KCC426V_PIN		.DB	0

.CSEG
	LDS	AH,PRE_KCC426V_PIN
	IN	AL,KCC426V_PIN
	ANDI	AL,KCC426V_PIN_MSK
	CP	AH,AL							// if PIN is not changed, goto CHECK_ENCODER_AB
	BREQ	MAIN_LOOP_CHECK_AB
	STS	PRE_KCC426V_PIN,AL
	MOV	BL,AL							// keep value in BH:BL
	RCALL	WAIT_FOR_CHATTERING
	EOR	AH,AL
	SBRC	AH,KCC426V_STEREO_SIG
	RCALL	STEREO_SIGNAL				// stereo signal is changed
	MOV	AL,BL
	SBRC	AH,KCC426V_TUNED_SIG
	RCALL	TUNE_SIGNAL					// Tune signal is changed
 
STEREO_SIGNAL:
	ANDI	AL,1 << KCC426V_STEREO_SIG
	RCALL	DISPLAY_STEREO
	RET

DISPLAY_STEREO:
	PUSH	AH
	PUSH	AL
	LDI	AL,STEREO_XPOS
	LDI	AH,STEREO_YPOS
	RCALL	LCD_GOTOXY
	POP	AL
	TST	AL
	BRNE	DISPLAY_STEREO_DELETE
	LDI	AL,'S'
	RCALL	LCD_PUT_CHAR
	LDI	AL,'T'
	RCALL	LCD_PUT_CHAR
	POP	AH
	RET
DISPLAY_STEREO_DELETE:
	LDI	AL,' '
	RCALL	LCD_PUT_CHAR
	RCALL	LCD_PUT_CHAR
	POP	AH
	RET

TUNE_SIGNAL:
	ANDI	AL,1 << KCC426V_TUNED_SIG
	RCALL	DISPLAY_TUNED
	RET

DISPLAY_TUNED:
	PUSH	AH
	PUSH	AL
	LDI	AL,TUNED_XPOS
	LDI	AH,TUNED_YPOS
	RCALL	LCD_GOTOXY
	POP	AL
	TST	AL
	BRNE	DISPLAY_TUNED_DELETE
	LDI	AL,'T'
	RCALL	LCD_PUT_CHAR
	LDI	AL,'u'
	RCALL	LCD_PUT_CHAR
	LDI	AL,'n'
	RCALL	LCD_PUT_CHAR
	LDI	AL,'e'
	RCALL	LCD_PUT_CHAR
	POP	AH
	RET
DISPLAY_TUNED_DELETE:
	LDI	AL,' '
	RCALL	LCD_PUT_CHAR
	RCALL	LCD_PUT_CHAR
	RCALL	LCD_PUT_CHAR
	RCALL	LCD_PUT_CHAR
	POP	AH
	RET


위에서 사용한 엔코더에는 push switch가 포함되어 있습니다. 다음 글에서는 엔코더의 push switch를 사용하는 방법에 관하여 다루어 보겠습니다. 단순한 click과 double click, 긴 click을 구분해서 각각 다른 기능을 수행할 수 있도록 해 보겠습니다.

블로그 이미지

엠쿠스

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

,