728x90
자주 보던 디지털 시계, 이것을 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 |