전 글에 이어서 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을 구분해서 각각 다른 기능을 수행할 수 있도록 해 보겠습니다.
'AVR > 작품' 카테고리의 다른 글
적외선(IR) 신호 읽기 - ATmega8 (0) | 2019.10.22 |
---|---|
Click, Double Click, Long Click 구분하기 (0) | 2019.09.07 |
KCC426V와 ATtiny2313을 이용한 FM radio 만들기(2) (0) | 2019.05.12 |
KCC426V와 ATtiny2313을 이용한 FM radio 만들기(1) (0) | 2019.05.12 |
자동차 실내 공기 살균기(CABUS) (0) | 2018.11.09 |