Harman 세미콘 아카데미/Atmega128a

2024.6.4 이전(3) [ATmega 128a]④ - FND

U_Pong 2024. 6. 18. 18:30

< FND >

 

7세그먼트 표시장치를 이번에 ATmega 128a와 연결해본다.

 

7세그먼트 표시장치는 7개의 선분으로 숫자나 글자를 표시하기 위해 발광 다이오드를 사용하여 만든 출력장치의 일종이다.

7개의 선분에 소수점을 표시하는 LED를 추가하여 8개의 세그먼트로 구성하는 것이 일반적이다.

7세그먼트 표시장치는 FND라고도 불리며, 한자리 뿐 아니라 두자리 이상도 표시할 수 있는 제품도 있다.

한자리 표시장치 - 10개 핀, 4자리 표시장치 - 12개 핀

 

1. 한자리 7세그먼트 표시장치

공통 핀: 항상 Vcc나 Gnd 중 하나가 가해진다.(2개)

제어 핀: 해당 세그먼트를 켜거나 끄기 위해 Vcc나 Gnd 중 하나의 전압을 선택하여 가한다.(8개)

공통 양극 방식: 공통 핀에 Vcc를 연결하고 제어 핀을 Gnd에 연결하면 해당 세그먼트가 켜지는 방식

공통 음극 방식: 공통 핀을 Gnd에 연결하고 제어 핀을 Vcc에 연결하면 해당 세그먼트가 켜지는 방식

 

공통 양극 방식과 공통 음극 방식은 제어하는 방식이 서로 반대이므로 어떤 방식인지 먼저 확인해야한다.

 

 
왼쪽은 공통 양극 방식, 오른쪽은 공통 음극 방식

 

소수점이 있는 면의 가장 왼쪽 핀이 1번핀이고, 반시계 방향으로 핀 번호가 증가한다.

 

7세그먼트 표시장치에 숫자를 표시하는 예는 아래의 사진과 같다.

7개의 세그먼트를 개별적으로 켜거나 꺼야 하므로 7개의 범용 입출력 핀이 필요하다.

 

 

 

 

숫자를 표시하기 위해 공통 음극 방식의 7세그먼트 표시장치 제어핀으로 출력하는 데이터는 아래와 같다.

DP는 소수점을 나타낸다.(1: on, 0: off)

 

한자리 7세그먼트 소스코드
/*
 * FND.c
 */ 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    uint8_t FND_number[] = 
	{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x67};	
	
	DDRA = 0xff;	// 출력 방향 설정
		
	// ATmega에서 int형은 2Byte임	(0~65535)
	int count = 0;	// index 를 가져오기 위한 변수
			
    while (1) 
    {
		PORTA = FND_number[count];	// 원소값을 PORTA 대입
		count = (count + 1) % 10;	// 1자리수만 표현 하기 위함
		_delay_ms(400);
    }
}

위의 소스코드는 한자리 7세그먼트가 0부터 9까지의 숫자를 순차적으로 나타내는 것을 구현한 것이다.

 

FND_한자리 7세그먼트 결과

INT_FND 소스코드
/*
 * INT_FND.c
 */ 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

int count = 0;
uint8_t FND_number[] =
{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x67};

ISR(INT0_vect)
{
	PORTA = FND_number[count];	// 원소값을 PORTA 대입
	count = (count + 1) % 10;	// 1자리수만 표현 하기 위함
}

ISR(INT1_vect)
{
	count =  0;
	PORTA = FND_number[count];
}

ISR(INT2_vect)
{
	PORTA = FND_number[count];	// 원소값을 PORTA 대입
	count = (count + 1) % 10;	// 1자리수만 표현 하기 위함
	_delay_ms(200);
}


int main(void)
{
	DDRA = 0xff;
	DDRD = 0x00;	// 입력 방향 설정(버튼)
	EICRA |= (1<<ISC11) | (1<< ISC01) | (1<<ISC00);
	EIMSK |= (1<<INT2) | (1<<INT1) | (1<<INT0);
	sei();
	
	PORTA = FND_number[0];
	
	while (1)
	{
	}
}

 

공통 음극 방식의 7세그먼트 표시장치 제어핀으로 출력하는 데이터 표를 참고하여 핀의 위치를 정한다.

각 버튼에 기능을 추가하여 순차적으로 숫자 올리기, 초기화시키기, 계속 누르고 있으면 숫자 올리기를 실행한다.

 

 

INT_FND 결과

 

 

2. 4자리 7세그먼트 표시장치

 

4자리 7세그먼트 표시장치를 제어하기 위해서 32개의 범용 입출력 핀이 필요하다.

자릿수가 증가할수록 피요로 하는 범용 입출력 핀의 수도 증가하는데, 적은 수의 범용 입출력 핀만을 사용하여 많은 수의 세그먼트를 제어할 수 있는 방법이 필요하다.

이때, 잔상 효과를 이용하면 된다.

 

잔상 효과: 사람의 눈은 반응 속도가 느려 눈앞의 물건이 사라진 이후에도 잠깐 동안은 물건이 사라진 것을 눈치채지 못한다. 망막에 맺힌 상은 짧은 시간 동안 그대로 유지되는 현상을 잔상효과라고 한다.

 

잔상효과를 이용하여 빠른 속도로 4자리 숫자를 반복적으로 켜 준다면 사람의 눈은 4자리 숫자가 동시에 표시되는 것으로 받아들인다.

4자리 7세그먼트 표시장치 역시 한 자리 7세그먼트와 마찬가지로 소수점이 있는 면을 아래로 했을 때 가장 왼쪽 핀이 1번이며 반시계 방향으로 핀 번호가 증가한다.

 

4자리 7세그먼트의 핀 번호

 

12개의 핀 중 8개는 8개의 세그먼트 제어를 위해 사용되며, 나머지 4개는 4자리의 숫자 중 각 자리를 선택하기 위해 사용된다.

4자리 7세그먼트 표시장치의 회로도는 아래와 같다.

 위쪽 4개 핀은 4자리 숫자 중 하나를 선택하기 위해 사용되는 핀이다.

즉, 12번은 ATmega 128a의 PORTG의 0번

9번은 ATmega 128a의 PORTG의 1번

8번은 ATmega 128a의 PORTG의 2번

6번은 ATmega 128a의 PORTG의 3번

와 같이 핀을 꽂아주면 된다.

 

아래쪽 8개 핀은 세그먼트 제어 핀으로 숫자는 핀 번호를 나타낸다.

즉, 11번은 ATmega 128a의 연결하고자 하는 PORTn의 0번

7번은 ATmega 128a의 연결하고자 하는 PORTn의 1번

4번은 ATmega 128a의 연결하고자 하는 PORTn의 2번

.

.

.

와 같이 핀을 꽂아주면 된다.

세그먼트 중에서 켜고 싶은 핀에는 1의 값을, 끄고 싶은 핀에는 0의 값을 가해주면 된다.

 

4자리 7세그먼트에 여러 자리에 서로 다른 숫자를 출력하기 위해서는 잔상 효과를 이용하여 빠른 속도로 4자리에 각기 다른 숫자를 출력하는 작업을 반복해야 한다.

 

FND_4Digit 소스코드

 

/*
 * FND_4Digit.c
 */ 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define FND_DATA_DDR	DDRC
#define FND_SELECT_DDR	DDRG
#define FND_DATA_PORT	PORTC
#define FND_SELECT_PORT	PORTG

void FND_Display(uint16_t data)
{
	static uint8_t position;
	uint8_t fndDaata[10]=
	{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x67};
	
	switch(position)
	{
		case 0:
		// 첫번째 자리의 FND를 출력하기 위해 0 LOW / 1,2,3 HIGH
		FND_SELECT_PORT &= ~(1<<PORTG0);
		FND_SELECT_PORT |= (1<<PORTG1) | (1<<PORTG2) | (1<<PORTG3);
		// 주어진 data에서 천의 자리를 구함
		FND_DATA_PORT = fndDaata[data/1000];
		break;
		
		case 1:
		// 두번째 자리의 FND를 출력하기 위해 1 LOW / 0,2,3 HIGH
		FND_SELECT_PORT &= ~(1<<PORTG1);
		FND_SELECT_PORT |= (1<<PORTG0) | (1<<PORTG2) | (1<<PORTG3);
		// 주어진 data에서 백의 자리를 구함
		FND_DATA_PORT = fndDaata[data/100%10];
		break;
		
		case 2:
		// 세번째 자리의 FND를 출력하기 위해 2 LOW / 0,1,3 HIGH
		FND_SELECT_PORT &= ~(1<<PORTG2);
		FND_SELECT_PORT |= (1<<PORTG0) | (1<<PORTG1) | (1<<PORTG3);
		// 주어진 data에서 십의 자리를 구함
		FND_DATA_PORT = fndDaata[data/10%10];
		break;
		
		case 3:
		// 네번째 자리의 FND를 출력하기 위해 3 LOW / 0,1,2 HIGH
		FND_SELECT_PORT &= ~(1<<PORTG3);
		FND_SELECT_PORT |= (1<<PORTG0) | (1<<PORTG1) | (1<<PORTG2);
		// 주어진 data에서 일의 자리를 구함
		FND_DATA_PORT = fndDaata[data%10];
		break;
	}
	position++;	// 다음 자릿수 이동을 위해
	position = position % 4;	// 4자리 출력후에 다시 첫번째 자리로 가기위해
}


int main(void)
{
	FND_DATA_DDR = 0xff;
	FND_SELECT_DDR = 0xff;
	FND_SELECT_PORT = 0x00;
	
	uint16_t count = 0;
	uint32_t timeTick = 0;
	uint32_t prevTime = 0;
	
	while (1)
	{
		FND_Display(count);
		if (timeTick - prevTime > 100) // 100ms 마다 count를 1씩 증가
		{
			prevTime = timeTick;
			count++;
		}
		_delay_ms(1);
		timeTick++;
	}
}

 

FND_4Digit 결과

FND 끝!

이 이후부터는 수업에 참여하여 직접 수업을 들었기 때문에 '2024.6.4 이전' 이라는 말이 제목에서 빠질 것이다.