전 글의 하드웨어적인 내용에 이어서 이 글에서는 KCC426V를 제어하는 소프트웨어를 다루고자 합니다. 전자공학적인 자세한 내용은 잘 모르니 통과합니다.
프로그래밍 도구로는 AVR Studio 4.19를 사용했으며, 프로그래밍 언어는 어셈블리어입니다. 부품을 가장 적게 사용해서 ATtiny2313을 동작시키기 위해, 클럭 소스를 internal RC oscillator 8MHz를 사용합니다. 다음은 AVR Studio 4.19의 ATtiny2313 퓨즈비트 설정 화면입니다.
1. BU2618로 명령 전송하기
KCC426V 모듈에서 LA1827 칩이 특정 주파수를 튜닝하기 위해서 BU2618을 사용하고 있습니다. 사용자가 BU2618에 적당한 값을 보내면 BU2618이 LA1827에게 적절한 값을 설정해서 특정 주파수의 방송을 청취할 수 있습니다. 특정 주파수를 수신하기 위해서 BU2618에 보내는 값은 다음과 같이 계산합니다.
결과값 = (수신 주파수 + 10700) / 25 / 2
주파수 단위는 KHz
예를 들어 93.1MHz 방송을 청취하려면
(93100 + 10700) / 25 / 2 = 2076 (16진수로는 0x081C)의 값을 보내면 됩니다. 이 계산식을 역으로 분석하면 BU2618로 보내는 결과값을 1만큼 변화시키면, 수신되는 주파수는 50KHz 바뀐다는 사실을 알 수 있습니다.
다음은 BU2618의 매뉴얼에 있는 타이밍 챠트입니다.
전체적인 내용을 분석해 보면 BU2618을 제어하기 위해서는 총 32비트의 정보를 전송해야 합니다. 앞의 16비트는 앞에서 계산한 주파수 설정을 위한 값이고, 뒤의 16비트는 환경 설정 값입니다. 환경 설정 값은 딱히 고칠 필요가 없으므로 모두 0으로 전송하겠습니다.
BU2618의 매뉴얼을 살펴보면 R0, R1, R2의 값을 바꾸어서 변하는 주파수의 값이 50KHz이던 것을 다른 값으로 설정할 수 있습니다. CT, output, S, PS, GT, TS 등에 관한 설명도 있는기는 합니다. 다만 굳이 이런 변수들을 변경시켜야할 필요도 없으며, 매뉴얼 자체에 에러도 있고 무슨 뜻인지 이해하기 어려운 것들이 많아 다른 변수들은 다 무시하기로 합니다.
데이터를 전송하려면
1) CE(Chip Enable)를 1로 설정 후 1.5uS 대기
2) 데이터 설정
3) CK(clock)을 1로 설정 후 1uS 대기
4) CK를 0으로 설정
5) 2)단계부터 4)단계를 반복하여 총 32비트 전송
6) CE를 0으로 설정
하면 됩니다.
데이터 전송할 때에 주파수 설정값을 0비트부터 시작하여 15비트까지 보낸다는 점에 유념할 필요가 있습니다. 데이터 전송을 위해서 앞 글의 하드웨어 연결 상태에 맞추어 다음과 같이 매크로를 정의합니다.
.EQU KCC426V_PORT = PORTB .EQU KCC426V_PIN = PINB .EQU KCC426V_DDR = DDRB .EQU KCC426V_EN = PB0 .EQU KCC426V_CK = PB1 .EQU KCC426V_DI = PB2 .EQU KCC426V_DO = PA0
KCC426V_DO는 아직까지 사용하지는 않지만 나중에 사용할 필요가 있을 때를 대비하여 연결해 두었습니다. 저 핀을 떼고 Transistor를 하나 추가하여 LCD의 백라이트를 관리할까 하는 생각을 가지고 있습니다.
실제로 데이터를 전송하는 부분을 작성해 봅니다.
////////////////////////////////////////////////// // SEND_TO_KCC426V: // PARAM AH:AL frequence digital value, BH:BL config data // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// SEND_TO_KCC426V: PUSH AL PUSH AH PUSH BL PUSH BH PUSH CL SBI KCC426V_PORT,KCC426V_EN LDI CL,16 SEND_TO_KCC426V_FREQUENCE_LOOP: SBRS AL,0 CBI KCC426V_PORT,KCC426V_DI SBRC AL,0 SBI KCC426V_PORT,KCC426V_DI CBI KCC426V_PORT,KCC426V_CK RCALL DELAY_1US SBI KCC426V_PORT,KCC426V_CK RCALL DELAY_1US LSR AH ROR AL DEC CL TST CL BREQ SEND_TO_KCC426V_CONFIG_DATA RJMP SEND_TO_KCC426V_FREQUENCE_LOOP SEND_TO_KCC426V_CONFIG_DATA: LDI CL,16 SEND_TO_KCC426V_CONFIG_DATA_LOOP: SBRS BL,0 CBI KCC426V_PORT,KCC426V_DI SBRC BL,0 SBI KCC426V_PORT,KCC426V_DI CBI KCC426V_PORT,KCC426V_CK RCALL DELAY_1US SBI KCC426V_PORT,KCC426V_CK RCALL DELAY_1US LSR BH ROR BL DEC CL TST CL BREQ SEND_TO_KCC426V_QUIT RJMP SEND_TO_KCC426V_CONFIG_DATA_LOOP SEND_TO_KCC426V_QUIT: CBI KCC426V_PORT,KCC426V_EN POP CL POP BH POP BL POP AH POP AL RET
위의 SEND_to_KCC462V 함수는 AH:AL에 주파수 설정 정보와 BH:BL에 환경 설정 값(실제로는 0)을 전달 받아서, 하위 비트의 값을 전송하고 값을 오른쪽으로 한 비트씩 시프트하여 각각 16비트씩, 총 32비트를 전송합니다.
2. 문자형 LCD에 정보 표시하기
문자형 LCD에 문자를 표시하는 방법은 에서 자세히 다루었습니다. 링크한 글에서와 달리 컨트롤러가 2개 있는 4행짜리 LCD가 니라 컨트롤러가 1개 밖에 없는 2행짜리 LCD이기 때문에 필요없는 내용들을 제거했습니다. 또한 링크한 글에서는 AVR의 IO 포트 D4~D7까지의 비트를 사용했지만, 이글에서는 D0~D3까지를 사용하기 때문에 코드가 약간 달라졌습니다.
앞 글의 하드웨어 연결 상태에 맞추어 다음과 같이 매크로를 정의합니다.
.EQU LCD_PORT = PORTD .EQU LCD_PIN = PIND .EQU LCD_DDR = DDRD .EQU LCD_RS_BIT = PD4 .EQU LCD_RW_BIT = PD5 .EQU LCD_E_BIT = PD6 .EQU LCD_DDR_VALUE = (0x0F | (1 << LCD_RS_BIT) | (1 << LCD_RW_BIT) | (1 << LCD_E_BIT))
위의 매크로를 사용하여 명령 전송 함수 LCD_WRITE_COMMAND, 데이터 전송 함수 LCD_WRITE_DATA, 문자 출력 함수 LCD_PUT_CHAR, 데이터 읽기 함수 LCD_READ_DATA, 상태 읽기 함수 LCD_READ_BF_ADDRESS, 초기화 함수 LCD_INIT, 화면 좌 상단으로 코서를 옮기는 LCD_RETURN_HOME, 화면을 지우는 LCD_CLEAR_DISPLAY, 커서의 형태를 정하는 LCD_SET_CURSOR, 화면 출력 좌표를 지정하는 함수 LCD_GOTOXY, 화면을 SHIFT 시키는 함수 LCD_SHIFT, 문자열 표시 함수 LCD_PUT_STRING 등을 작성합니다.
////////////////////////////////////////////////// // LCD_WRITE_COMMAND_8Bit: // PARAM AL:COMMAND // RETURN NONE // CHANGED NONE ////////////////////////////////////////////////// LCD_WRITE_COMMAND_8Bit: CBI LCD_PORT,LCD_RS_BIT SWAP AL // D0-D3 OUT LCD_PORT,AL CBI LCD_PORT,LCD_RW_BIT SBI LCD_PORT,LCD_E_BIT RCALL DELAY_100US CBI LCD_PORT,LCD_E_BIT // E = 0 RCALL DELAY_1MS RET ////////////////////////////////////////////////// // LCD_WRITE_COMMAND: // PARAM AL:COMMAND // RETURN NONE // CHANGED NONE ////////////////////////////////////////////////// LCD_WRITE_COMMAND: CBI LCD_PORT,LCD_RS_BIT // RS = 0 RJMP LCD_WRITE ////////////////////////////////////////////////// // LCD_WRITE_DATA: // PARAM AL:COMMAND // RETURN NONE // CHANGED NONE ////////////////////////////////////////////////// LCD_WRITE_DATA: LCD_PUT_CHAR: SBI LCD_PORT,LCD_RS_BIT // RS = 1 LCD_WRITE: PUSH AH CBI LCD_PORT,LCD_RW_BIT // RW = 0 IN AH,LCD_PIN ANDI AH,0x70 PUSH AL SWAP AL // 상위 4비트, 하위4비트 바꿈 ANDI AL,0x0F OR AL,AH OUT LCD_PORT,AL // 데이터 출력 SBI LCD_PORT,LCD_E_BIT // E = 1 상위 4비트 전달 RCALL DELAY_1US CBI LCD_PORT,LCD_E_BIT // E = 0 RCALL DELAY_100US POP AL ANDI AL,0x0F OR AL,AH OUT LCD_PORT,AL // 데이터 출력 SBI LCD_PORT,LCD_E_BIT // E = 1 하위 4비트 전달 RCALL DELAY_1US CBI LCD_PORT,LCD_E_BIT // E = 0 RCALL DELAY_100US POP AH RET ////////////////////////////////////////////////// // LCD_READ_DATA: // PARAM NONE // RETURN AL // CHANGED AL ////////////////////////////////////////////////// LCD_READ_DATA: SBI LCD_PORT,LCD_RS_BIT // RS = 1 RJMP LCD_READ ////////////////////////////////////////////////// // LCD_READ_BF_ADDRESS: // PARAM NONE // RETURN AL // CHANGED AL ////////////////////////////////////////////////// LCD_READ_BF_ADDRESS: CBI LCD_PORT,LCD_RS_BIT // RS = 0 LCD_READ: PUSH AH LDI AH,(1 << LCD_RS_BIT) | (1 << LCD_RW_BIT) | (1 << LCD_E_BIT) // set LCD_DATA_PORT input OUT LCD_DDR,BL SBI LCD_PORT,LCD_RW_BIT // RW = 1 SBI LCD_PORT,LCD_E_BIT // E = 1 RCALL DELAY_100US IN AH,LCD_PIN // read data from LCD_DATA_PIN CBI LCD_PORT,LCD_E_BIT // E = 0 ANDI AH,0x0F // 상위 4비트를 레지스터 AL에 저장 RCALL DELAY_100US SBI LCD_PORT,LCD_E_BIT // E = 1 RCALL DELAY_100US IN AL,LCD_PIN CBI LCD_PORT,LCD_E_BIT // E = 0 ANDI AL,0x0F SWAP AH // 하위 4비트로 OR AL,AH // 상위 4비트를 합침 LDI AH,LCD_DDR_VALUE OUT LCD_DDR,AH RCALL DELAY_100US POP AH RET ////////////////////////////////////////////////// // LCD_INIT: // PARAM NONE // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_INIT: LDI AL,50 //ㄱ) 전원 안정화를 위해 50mS 대기 RCALL DELAY_MS LDI AL,LCD_DDR_VALUE OUT LCD_DDR,AL CLR AH OUT LCD_PORT,AH OUT KCC426V_PORT,AH LDI AL,LCD_FUNCTIONSET | LCD_8BITMODE // ㄴ) Function Set 명령 RCALL LCD_WRITE_COMMAND_8BIT LDI AL,5 // 5mS 대기 RCALL DELAY_MS LDI AL,LCD_FUNCTIONSET | LCD_8BITMODE // ㄷ) Function Set 명령 RCALL LCD_WRITE_COMMAND_8BIT LDI AL,5 // 5mS 대기 RCALL DELAY_MS LDI AL,LCD_FUNCTIONSET | LCD_8BITMODE // ㄹ) Function Set 명령 RCALL LCD_WRITE_COMMAND_8BIT // ㅁ) 4BIT 제어를 위한 Function Set 명령 LDI AL,LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS RCALL LCD_WRITE_COMMAND_8BIT RCALL LCD_RETURN_HOME // ㅂ) Return Home 명령 RCALL LCD_CLEAR_DISPLAY // ㅅ) Clear Display 명령 LDI AL,LCD_ENTRYMODESET | LCD_ENTRY_RIGHT | LCD_ENTRY_NOSHIFT RCALL LCD_WRITE_COMMAND // ㅇ) 커서 오른쪽 이동 Entry Mode Set LDI AL,LCD_SETDDRAMADDR // ㅈ) DDRAM 어드레스 카운터 0 RCALL LCD_WRITE_COMMAND LDI AL,LCD_DISPLAYCONTROL | LCD_DISPLAYON // ㅊ) Display ON/OFF Control 명령 RJMP LCD_WRITE_COMMAND ////////////////////////////////////////////////// // LCD_RETURN_HOME: // PARAM NONE // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_RETURN_HOME: LDI AL,LCD_RETURNHOME RCALL LCD_WRITE_COMMAND LDI AL,3 RJMP DELAY_MS ////////////////////////////////////////////////// // LCD_CLEAR_DISPPLAY: // PARAM NONE // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_CLEAR_DISPLAY: LDI AL,LCD_CLEARDISPLAY RCALL LCD_WRITE_COMMAND LDI AL,3 RJMP DELAY_MS ////////////////////////////////////////////////// // LCD_SET_CURSOR: // PARAM AL:(on:LCD_CURSORON | LCD_BLINKON, off:0) // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_SET_CURSOR: ORI AL,LCD_DISPLAYCONTROL | LCD_DISPLAYON RJMP LCD_WRITE_COMMAND ////////////////////////////////////////////////// // LCD_GOTOXY: // PARAM AL:x, AH:y // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_GOTOXY: SBRC AH,0 ORI AL,0x40 // add 0x40 if y = 1 or 3 ORI AL,LCD_SETDDRAMADDR RJMP LCD_WRITE_COMMAND ////////////////////////////////////////////////// // LCD_SHIFT: // PARAM AL:LCD_MOVELEFT,LCD_MOVERIGHT // RETURN NONE // CHANGED AL ////////////////////////////////////////////////// LCD_SHIFT: LDI AL,1 << LCD_E_BIT ORI AL,LCD_CURSORSHIFT | LCD_DISPLAYMOVE RJMP LCD_WRITE_COMMAND ////////////////////////////////////////////////// // LCD_PUT_STRING: // PARAM Z:POINTER // RETURN NONE // CHANGED AL,Z ////////////////////////////////////////////////// LCD_PUT_STRING: LD AL,Z+ TST AL BREQ LCD_PUT_STRING_QUIT RCALL LCD_PUT_CHAR RJMP LCD_PUT_STRING LCD_PUT_STRING_QUIT: RET
이상으로 (2)편을 마치고 다음 글에서 엔코더의 움직임을 감지하여 주파수를 올리고 내리는 작업과, tuning 상태와 stereo 상태에 따라 해당 LED를 점등 및 소등 시키는 작업을 진행하겠습니다.
'AVR > 작품' 카테고리의 다른 글
Click, Double Click, Long Click 구분하기 (0) | 2019.09.07 |
---|---|
KCC426V와 ATtiny2313을 이용한 FM radio 만들기(3) (0) | 2019.06.24 |
KCC426V와 ATtiny2313을 이용한 FM radio 만들기(1) (0) | 2019.05.12 |
자동차 실내 공기 살균기(CABUS) (0) | 2018.11.09 |
30V 2채널 전압 측정기 겸 signal 발생기 (0) | 2018.11.09 |