728x90

 

7 Segment

자주 보던 디지털 시계, 이것을 7 Segment라고 합니다. 자릿수에 따라 7개의 Segment 표시 장치가 존재하기 때문입니다.


7 Segment

7 Segment는 양극형, 음극형으로 나뉩니다.

또한 Segment 사용을 위해서는 위와 같은 제어값을 활용하여 값을 출력할 수 있습니다.

     

위와 같이 회로를 구성합니다.

저는 그림과 같게 C와 D Port를 이용하여 7 Segment를 연결했습니다.

 

Interrupt를 이용하여 타이머에 변화를 줄 것입니다.

각각의 스위치는 Interrupt를 발생시킵니다. E Port의 4, 5, 6번을 왼쪽부터 연결하여 사용하였습니다. 각각의 Interrupt가 발생하면 특정 변수에 변화를 주는 방식으로 프로그래밍을 진행하였습니다.

 

4번(좌)시작/멈춤 그리고 5번(중)을 눌러 자릿수를 선택할 때 숫자를 키우는 역할입니다. 6번(우)은 리셋버튼 입니다.

ISR(INT4_vect)										// 왼쪽(적색) 스위치
{		
	if(state == 1)									// state 1이면 1의 자리 증가
	{
		tcnt++;
		if((tcnt % 10) == 0) tcnt-=10;				// 9에서 10으로 넘어가면 -10
	}	
	else if(state == 2)								// state 2이면 10의 자리 증가
	{
		(tcnt += 10);
		if(((tcnt / 10) % 6) == 0) tcnt-=60;		// 5에서 6으로 넘어가면 -60
	}		
	else if(state == 3)								// state 3이면 세번째 자리 증가
	{
		(tcnt += 60);\
		if(((cnt / 60) % 10) == 0) tcnt-=600;		// 9에서 10으로 넘어가면 -600
	}		
	else if(state == 4)								// state 4이면 네 번째 자리 증가
	{
		(tcnt += 600);	
		if(((tcnt / 600) % 10) == 0) tcnt-=6000;	// 5에서 6으로 넘어가면 -6000
	}	
	else if((state == 0) && (opt > 4))	opt = 0;
	else
	{
		opt++;
		if (opt >= OPTMAX) opt = 0;
	}
}
ISR(INT5_vect)										// 가운데(주황색) 스위치
{	
	state++;
	if (state >= STATEMAX) state = 0;
}
ISR(INT6_vect)										// 오른쪽(노란색) 스위치
{	
	reset++;
	state = 0;
	opt = 0;
	wdt_enable(WDTO_15MS);							//Reset
	wdt_reset();
	if (reset >= RESETMAX) 
	{
		reset = 0;
		cnt = 0, tcnt = 0;
	}
}

 

전체 회로도의 모습입니다. 위의 LED는 타이머가 설정시간에 도달되었을 때 알림용으로 사용합니다.

설정 시간 도달 시 LED 점등 영상입니다.

 

 

첫번째 버튼을 클릭해서 타이머를 시작하는 모습입니다.

 

 

전체적인 작동 영상입니다.

 

#include "myHeader.h"		//""는 동일 디렉토리 안에서 호출 시
#define OPTMAX 10
#define STATEMAX 5
#define RESETMAX 2

#include <avr/io.h>			//<>는 System header, 제공하는 header 약속되어 있는 곳에 있다.
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

#define __delay_t 500

volatile int opt = 0, state = 0, reset = 0;
unsigned long cnt = 0, tcnt = 0;			// cnt는 실제 카운트 tcnt는 설정 카운트



int main(void)
{
	PortSet(&PORTC, &PORTD);
	PORTE = 0x07;
	DDRA = 0x0F;
	DDRC = 0x0F;
	DDRD = 0xFF;
	DDRE = 0x00;

	EIMSK = 0x70;		// 0111 0000	//INT 4~INT 6 활성화
	EICRB = 0x2a;		//4개의 B그룹(INT4~INT7)의 인터럽트 발생 시점 결정(00 10 10 10, 각 7 6 5 4에서의 INT발생 시점을 rising edge로 결정)

	TIMSK |= 0x04;		// 16bit - 0000 0100b - Timer 2 TCNT overflow interrupt
	TCCR1B = 0x04;		// 분주비(Pre-scaler) 64

	SREG |= 0x80;		//status Register - 인터럽트 허용 상태 레지스터
	sei();
    while (1) 
    {
	PORTD = 0x3F;
	if((cnt == tcnt) && (cnt != 0))
	{
		while(reset == 0)
		{
			char m;
			Toggle(m);	// 시간에 도달하면 LED와 Segment가 함께 깜박이는 역할
		}
	}
	if(state != 0)		// state(가운데 = 숫자 변경용) 이 동작하면
	{
		while(0 < state && state < 5)	// 1 ~ 4동안 변경할 자릿 수 점등
		{
			for(int i = 0; i < 50; i++)	// delay를 주고 설정카운트 전체 점등
			AllDisp(tcnt);
			for(int i = 0; i < 50; i++)	// delay를 주고 설정 카운트 중 설정할 값 제외하고 점등
			AllDisp_state(tcnt); continue;
		}
	}
	else if(opt != 0)	// opt(첫번 째 = 시작/정지 버튼) 이 동작하면
	{
		switch(opt)
		{
			case 1:	
			for(int i = 0; i < 50; i++)	 DecDisp(cnt); continue;		// 1번 누르면 시작
			case 2: 
			for(int i = 0; i < 50; i++)	 DecDisp(cnt); continue;		// 2번 누르면 멈춤
			case 3: continue;											// 3번 누르면 0
			case 4:	wdt_enable(WDTO_15MS);								// 4번 누르면 reset
			default : opt = 0; break;
		}
	}

	else if(reset != 0)	//reset이 동작하면.
	{
		switch(reset)
		{
			case 1: cnt = 0, tcnt = 0; continue;						// 카운트된 값 초기화
			default: reset = 0; cnt = 0, tcnt = 0; break;				// 카운트된 값 초기화(안전용)
		}
	}
    }
}
ISR(TIMER1_OVF_vect)
{
	if((opt == 1) && (state == 0))	cnt++;			// opt만 1번 동작하고 state는 동작하지 않으면 cnt 올라간다.
}
ISR(INT4_vect)										// 왼쪽(적색) 스위치
{		
	if(state == 1)									// state 1이면 1의 자리 증가
	{
		tcnt++;
		if((tcnt % 10) == 0) tcnt-=10;				// 9에서 10으로 넘어가면 -10
	}	
	else if(state == 2)								// state 2이면 10의 자리 증가
	{
		(tcnt += 10);
		if(((tcnt / 10) % 6) == 0) tcnt-=60;		// 5에서 6으로 넘어가면 -60
	}		
	else if(state == 3)								// state 3이면 세번째 자리 증가
	{
		(tcnt += 60);\
		if(((tcnt / 60) % 10) == 0) tcnt-=600;		// 9에서 10으로 넘어가면 -600
	}		
	else if(state == 4)								// state 4이면 네 번째 자리 증가
	{
		(tcnt += 600);	
		if(((tcnt / 600) % 10) == 0) tcnt-=6000;	// 5에서 6으로 넘어가면 -6000
	}	
	else if((state == 0) && (opt > 4))	opt = 0;
	else
	{
		opt++;
		if (opt >= OPTMAX) opt = 0;
	}
}
ISR(INT5_vect)										// 가운데(주황색) 스위치
{	
	state++;
	if (state >= STATEMAX) state = 0;
}
ISR(INT6_vect)										// 오른쪽(노란색) 스위치
{	
	reset++;
	state = 0;
	opt = 0;
	wdt_enable(WDTO_15MS);							//Reset
	wdt_reset();
	if (reset >= RESETMAX) 
	{
		reset = 0;
		cnt = 0, tcnt = 0;
	}
}

Main_Timer 코드입니다.

 

아래는 함수를 주로 만들어둔 Segment.c 코드입니다.

#include "myHeader.h"

#include <avr/io.h>
#include <avr/delay.h>


uint8_t digit[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71};
uint8_t dec[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x27, 0x7F, 0x67};

char arr[5];		//segment image 정보를 담을 안전영역
char *PC = &PORTC, *PD = &PORTD;
extern int state;			//전역변수로 state를 가져옴 >> 시간 설정 시 깜박임 위해서
extern long cnt, tcnt;		//   ``		cnt와 tcnt도 가져옴
void PortSet(char *p1, char *p2)
{
	PC = p1; PD = p2;
}
void seg(int pos, uint8_t c)
{
	*PC |= 0x0F;
	*PC &= ~(1 << (3 - pos));

	*PD = c;
	_delay_ms(2);
}
int k = 4;

void FND_4(char* inf) // 필요한 자리만 segment Image 배열
{
	for(int i = 0; i < k; i++)
	{
		seg(i, *(inf + i));
		//seg(i, inf[i]);
	}
}

void seg_state(int pos, uint8_t c)	//seg와 seg_state를 교차하여 점등하기 위함
{
	if((state - 1) != pos)			//state -1 >> n번 동작시 n-1번째를 제외하고 점등
	{
		*PC |= 0x0F;
		*PC &= ~(1 << (3 - pos));

		*PD = c;
		_delay_ms(2);
	}
}


void FND_x(char* inf) // 4자리 모두 segment Image 배열
{
	for(int i = 0; i < 4; i++)
	{
		seg(i, *(inf + i));
	}
}

void FND_state(char* inf) // 점등을 위한 segment Image 배열
{
	for(int i = 0; i < 4; i++)
	{
		seg_state(i, *(inf + i));
	}
}

char* SegDisp(unsigned long num)		// 16 진수 함수
{	//10진 정수를 입력받아 16진수 문자열로 변환 ex)65535 ==> 0xffff, 56506=>0xBCDA
	int n1 = num % 16;			//A(10): 문자가 아닌 숫자
	int n2 = (num / 16) % 16;	//B(11)
	int n3 = (num / 256) % 16;	//C(12)
	int n4 = num / 4096;		//D(13)
	
	arr[0] = digit[n1]; arr[1] = digit[n2]; arr[2] = digit[n3]; arr[3] = digit[n4];
	
	if ( num<16 ){
		arr[3] = 0; arr[1] = 0; arr[2] = 0;
	}
	else if ( num<256 ){
		arr[2] = 0; arr[3] = 0;
	}
	else if ( num<4096 ){
		arr[3] = 0;
	}
	k = (num > 4095) ? 4 : (num > 256) ? 3 : (num > 16) ? 2 : 1;
	FND_4(arr);
	return arr;
}

char* DecDisp(unsigned long num)		// 기본 시간 함수
{	
	int n1 = num % 10;			
	int n2 = (num / 10) % 6;	
	int n3 = (num / 60) % 10;	
	int n4 = (num / 100) % 6;		
	
	arr[0] = dec[n1]; arr[1] = dec[n2]; arr[2] = dec[n3]; arr[3] = dec[n4];
	
	if ( num<10 ){
		arr[3] = 0; arr[1] = 0; arr[2] = 0;
	}
	else if ( num<60 ){
		arr[2] = 0; arr[3] = 0;
	}
	else if ( num<600 ){
		arr[3] = 0;
	}
	k = (num > 600) ? 4 : (num > 60) ? 3 : (num > 10) ? 2 : 1;
	FND_4(arr);
	return arr;
}

char* AllDisp(unsigned long num)		// 0인 자리까지 다 보여주는 함수
{	
	int n1 = num % 10;			
	int n2 = (num / 10) % 6;	
	int n3 = (num / 60) % 10;	
	int n4 = (num / 600) % 6;	
	
	arr[0] = dec[n1]; arr[1] = dec[n2]; arr[2] = dec[n3]; arr[3] = dec[n4];
	FND_x(arr);
	return arr;
}

char* AllDisp_state(unsigned long num)	// state 번호 제외 하고 다 보여주는 함수 > AllDisp와 교차로 사용해서 깜빡임
{	
	int n1 = num % 10;			
	int n2 = (num / 10) % 6;	
	int n3 = (num / 60) % 10;	
	int n4 = (num / 600) % 6;	
	
	arr[0] = dec[n1]; arr[1] = dec[n2]; arr[2] = dec[n3]; arr[3] = dec[n4];
	FND_state(arr);
	return arr;
}

void Toggle(char m)						// 설정 시간 도달 시 알림용 Toggle 함수
{
	for(int n = 0; n < 3; n++)
	{
		char m = 1 << n;
		PORTA |= m;
		_delay_ms(50);
		PORTA &= ~m;
		_delay_ms(50);
		for(int i = 0; i < 10; i++)
		AllDisp(tcnt);
		for(int i = 0; i < 10; i++)
		AllDisp_state(tcnt);
	}
}

ss

728x90

'( * )Engineering > 🌜C' 카테고리의 다른 글

Seminar (3) C  (0) 2025.03.16
Seminar (2) C  (0) 2025.03.13
Seminar (1) C  (0) 2025.03.11
[ATmega 128] 1. LED 점등  (1) 2024.03.29
[ATmega 128] 0. Microchip Studio 사용기(LED 점멸)  (0) 2024.03.21