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

전 글의 하드웨어적인 내용에 이어서 이 글에서는 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 제어 신호 보내기

전체적인 내용을 분석해 보면 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를 점등 및 소등 시키는 작업을 진행하겠습니다.

블로그 이미지

엠쿠스

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

,