
Button2.h

Button2.c

main.c

위 영상에서 보시다시피 1번 버튼은 LED on, 2번 버튼은 LED off, 3번 버튼은 상태를 반전시키는 기능을 한다.
Button_Init함수로 3개의 버튼을 초기화 시킨다. 헤더파일에 Button_ON,Button_OFF,Button_Toggle 3가지를 매크로 설정을하여 PORTD의 0번 PIN(PD0)는 ON, 1번PIN(PD1)는 OFF, 2번PIN(PD2)는 Toggle 기능을 한다.
Button2.c 코드를 보면 버튼을 눌렀을 때 실행되는 Button_getState()함수가 LED의 상태를 변경한다. 초기화 시킨 버튼들(구조체 변수로 pinNumber를 할당!)의 구조체 주소를 매개변수로 넘겨주어 해당 버튼이 눌렀을때, 버튼을 눌렀다 땠을때 2가지 경우로 하였다. 버튼을 누른 상태에서는 LED의 변화를 주지 않고 눌렀다 땠을 경우 enum으로 처리한 상태들을 변경시켜 LED를 점등했다. 이때 Button_getState()함수를 보면 _delay_ms(50);를 사용했다. 이는 실시간 시스템에서는 바람직하지 않을 수 있으며 CPU가 다른 중요한 작업을 처리하는 것을 방해할 수 있는데. 지금은 간단한 실습이니 딜레이함수로 처리했다. 이를 사용한 이유는 채터링 현상 때문이다.
#스위치를 누르면 채터링현상(바운스 현상)발생. 버튼이 눌렸을때 스위치가 동작을 하는데
전기의 기전력 때문에 주파수가 튀는 현상이 발생한다. 이를 채터링 현상이라고 함.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRC = 0xff;
DDRD &= ~(1<<DDRD0) |~(1<<DDRD1)|~(1<<DDRD2) ;
uint8_t ledData = 0x01;
uint8_t buttonData; //버튼 입력 받은 변수 설정
int flag = 0; //atmega128에서는 int가 2byte
PORTC = 0x00;
while (1)
{
buttonData = PIND;
if((buttonData &(1<<0))==0)
{
PORTC = ledData;
ledData = (ledData >> 7) | (ledData << 1);
_delay_ms(300);
}
else if((buttonData &(1<<1))==0)
{
PORTC = ledData;
ledData = (ledData >> 1) | (ledData << 6);
_delay_ms(300);
}
else if(flag == 0)
{
if((buttonData & (1<<2)) == 0)
{
flag = 1;
}
else
{
flag = 0;
}
}
else if(flag == 1)
{
for(uint8_t i = 0; i < 5; i++)
{
PORTC = 0xff;
_delay_ms(200);
PORTC = 0x00;
_delay_ms(200);
}
flag = 0;
}
}
}
PIN0~2를 입력으로 설정buttonData변수를 8비트 데이터 타입으로 설정하여 PIND로 할당하여 buttonData의 가장 낮은 비트(즉, 0번 비트)와 AND 연산을 수행합니다. PIND 레지스터에서 0번 핀(D0)의 상태를 확인합니다. 이 때 AVR 마이크로컨트롤러에서는 버튼을 누르면 해당 비트가 0으로 설정되는 경우가 많습니다(풀다운 저항을 사용하는 경우). 즉 눌렀다면 다음과 같이 LED를 한칸 이동시킵니다.PD2핀에 연결된 버튼을 누르면 flag가 1이 되어 아래 코드를 실행하여 LED전체가 깜빡거립니다.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.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 = 0;//Digit position 변수 설정 및 초기화
uint8_t fndData[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
switch(position)
{
case 0:
//portg = [pg0 = 0,pg1 = 1,pg2 = 1,pg3 = 1]
FND_SELECT_PORT &= ~(1<<0); //digit 1 = LOW
FND_SELECT_PORT |= (1<<1) | (1<<2) | (1<<3); //digit 2,3,4 High.
//입력된 데이터의 천의 자리를 구해서 fnd에 출력
FND_DATA_PORT = fndData[data/1000];
break;
//portg = [pg0 = 1,pg1 = 0,pg2 = 1,pg3 = 1]
case 1:
FND_SELECT_PORT &= ~(1<<1); //digit 2 = Low
FND_SELECT_PORT |= (1<<0) | (1<<2) | (1<<3); //digit = 1,3,4 High
FND_DATA_PORT = fndData[data/100%10];
break;
//portg = [pg0 = 1,pg1 = 1,pg2 = 0,pg3 = 1]
case 2:
FND_SELECT_PORT &= ~(1<<2); //digit 3 = LOW
FND_SELECT_PORT |= (1<<0) | (1<<1) | (1<<3); //digit 1,2,4 High
FND_DATA_PORT = fndData[data/10%10];
break;
//portg = [pg0 = 1,pg1 = 1,pg2 = 1,pg3 = 0]
case 3:
FND_SELECT_PORT &= ~(1<<3);
FND_SELECT_PORT |= (1<<0) | (1<<1) | (1<<2);
FND_DATA_PORT = fndData[data%10];
break;
}
position++;// 포지션 증가
position = position % 4; //4자리숫자가 최대이니 4자리 수 이상으로 넘어가지 않기위함.
}
int main(void)
{
// DDRG = 0xff;
// uint8_t seg[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
// int count = 0; //int type은 ATmega128에서는 2byte 0 ~ 65535.
// DDRC = 0xff;
// while (1)
// {
// PORTC = seg[count];
// count = (count + 1) % 16;
// _delay_ms(600);
// }
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)
{
prevTime = timeTick;
count++;
}
_delay_ms(1);
timeTick++;
}
}
FND_DATA_DDR을 DDRG 로 #define 해주어 출력으로 설정했습니다. Segment에 연결되는 포트는 PORTC 로 정해 FND_SELECT_DDR = DDRC ,FND_SELECT_PORT = PORTC 로 #define 해주었습니다. FND_Display() 는 switch 문으로 들어오는 시간의 천,백,십,일의 자리수를 계산하여 출력합니다. 이때 우리는 Common Cathode 타입을 사용했기 때문에 내가 출력해주고 싶은 segment, 해당 digit에 LOW 값으로 하여 Count를 계산하여 숫자를 표시합니다.main()에서 if(timeTick - prevTime > 100) 조건문은 timeTick과 prevTime 사이의 차이가 100ms 이상인지 확인합니다. 이는 약 100ms마다 한 번씩 count 변수를 증가시키기 위한 조건입니다. prevTime = timeTick;이는 다음 100ms를 세기 위한 시작점을 세팅합니다._delay_ms(1); 호출은 프로그램 실행을 약 1ms 동안 지연시킵니다. 이는 timeTick 변수를 정확히 1ms마다 증가시키기 위한 것입니다.timeTick++은 timeTick 변수의 값을 1씩 증가시켜, 시간의 흐름을 추적합니다.