Harman 세미콘 아카데미/Atmega128a

2024.6.4 이전(2) [ATmega 128a]③ - LED_div, Button, Button_toggle

U_Pong 2024. 6. 18. 18:00

< LED_div >

저번 블로그 글에서의 LED에 관하여 내용을 이어가보면, 이번에도 총 3개의 파일이 필요하다.

또한, 아래 사진과 같은 LED_bar를 사용하여 결과값을 확인한다.

 

/*
 * led.h
 */ 

#ifndef LED_H_
#define LED_H_

#include <avr/io.h>

typedef struct LED
{
	volatile uint8_t	*port;		// LED가 연결될 포트
	uint8_t				pinNumber;	// LED가 연결될 핀번호
}LED;	// 내가 만든 LED라는 데이터형

// 사용자 정의 함수
void ledInit(LED *led);
void ledOn(LED *led);
void ledOff(LED *led);


#endif /* LED_H_ */

 

/*
 * led.c
 */ 

#include "led.h"


void ledInit(LED *led)
{
	//DDRC = 0x01;	//포트에 대한 방향 설정 0b00000001
	//포트에 해당하는 핀을 출력으로 설정
	*(led->port - 1) |= (1<<led->pinNumber);
	//*(led->port - 1) = *(led->port - 1) | (1<<led->pinNumber)
	// DDR레지스터는 PORT레지스터보다 1낮게 위치하므로
	// *(led->port - 1)를 이용해서 PORT에서 DDR로 접근
}
void ledOn(LED *led)
{
	// 해당 핀을 켜기
	*(led->port) |= (1<<led->pinNumber);
	// 내가 원하는 위치에 1을 넣기 위해
}
void ledOff(LED *led)
{
	*(led->port) &= ~(1<<led->pinNumber);
	// 내가 원하는 위치에 0을 넣기 위해
}

 

/*
 * LED_div.c
 */ 

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

#include "led.h"

int main(void)
{
	LED ledBar; // 구조체 LED의 데이터형을 갖는 ledBar변수 선언
	ledBar.port = &PORTC;
	
	for (uint8_t i = 0; i < 8; i++)
	{
		ledBar.pinNumber = i;
		ledInit(&ledBar);
	}
	
	//ledBar.pinNumber = 5;
	//
	//ledInit(&ledBar);	// DDRC pinNumber핀을 출력으로 설정
	
	while (1)
	{
		for (uint8_t i = 0; i < 8; i++)
		{
			ledBar.pinNumber = i;
			ledOn(&ledBar);
			_delay_ms(100);
			//for (uint8_t i = 0; i < 8; i++)
			//{
			//ledBar.pinNumber = i;
			//ledOff(&ledBar);
			//}
		}
		_delay_ms(1000);
		for (uint8_t i =0; i < 8; i++)
		{
			ledBar.pinNumber  = i;
			ledOff(&ledBar);
			_delay_ms(100);
		}
		
		//for (uint8_t i = 0; i < 8; i++)
		//{
		//ledBar.pinNumber = i;
		//ledOn(&ledBar);
		//_delay_ms(100);
		//ledOff(&ledBar);
		//_delay_ms(100);
		//}
		
		//ledOn(&ledBar);
		//_delay_ms(300);
		//ledOff(&ledBar);
		//_delay_ms(300);
	}
}

 

저번의 LED_pointer와 다르게 이번에는 시간이 지나도 다른 LED가 꺼지는 것이 아니라

차례대로 모두 켜진후 유지했다가 다시 차례대로 모두 꺼지는 것을 구현하는 것이다.

LED_div 결과

 

 

< BUTTON >

이번에는 버튼을 이용하여 LED_bar가 작동되도록 구현한다.

버튼은 기본적으로 핀에 연결하여 버튼이 눌려지면 Vcc(논리 1), 버튼이 눌려지지 않으면 0이 가해지도록 한다.

버튼 입력을 읽어 들일때도 GPIO 핀을 입력 또는 출력으로 사용할 수 있도록 설정하는 DDRx 레지스터를 사용한다.

4핀 버튼은 대각선 방향에 있는 2개의 핀을 사용하는 것이 일반적이다.
 

 

/*
 * Button.c
 */ 

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

int main(void)
{
	DDRC = 0xff;	// LED연결된곳
	DDRD &= ~(1<<DDRD0);	// DDRD0번핀을 입력으로 설정
	DDRD &= ~(1<<DDRD1);	// DDRD1번핀을 입력으로 설정
	DDRD &= ~(1<<DDRD2);	// DDRD2번핀을 입력으로 설정
	
	//PORTD |= (1<<PORTD0);	// 내부풀업 활성화
	
	uint8_t ledData = 0x01;
	uint8_t buttonData;		// PIND
	PORTC = 0x00;	// LED 꺼진 상태로 출발해서
	
	while (1)
	{
		buttonData = PIND;
		
		// 0번핀 버튼 눌르면 LED 좌측이동(delay 300) 하고 꺼짐
		if ((buttonData & (1<<0)) == 0)
		{
			// 비트시프트
			for (uint8_t i = 0; i < 8; i++)
			{
				PORTC = ledData;
				ledData = (ledData >> 7) | (ledData << 1);
				_delay_ms(300);
			}
			PORTC = 0x00;
		}
		// 1번핀 버튼 눌르면 LED 우측이동(delay 300) 하고 꺼짐
		if ((buttonData & (1<<1)) == 0)
		{
			// 비트시프트
			for (uint8_t i = 0; i < 8;i++)
			{
				PORTC = ledData;
				ledData = (ledData >> 1) | (ledData << 7);
				_delay_ms(300);
			}
			PORTC = 0x00;
		}
		// 2번핀 버튼 눌르면 LED 점멸 3번(delay 300)
		if ((buttonData & (1<<2)) == 0)
		{
			for (uint8_t i = 0; i < 3; i++)
			{
				PORTC = 0xff;
				_delay_ms(300);
				PORTC = 0x00;
				_delay_ms(300);
			}
		}
	}
}

 

소스코드에서는 0번 버튼을 눌렀을때 LED가 좌측으로 순서대로 켜졌다 꺼짐을 반복하고

1번 버튼을 눌렀을때 LED가 우측으로 순서대로 켜졌다 꺼짐을 반복하고

2번핀을 눌렀을때 LED 8개 모두 점멸을 3번하는 것을 구현했다.

 

 

Button 0, 1, 2 결과

 

 

< Button_toggle >

이번에는 버튼 0번은 LED가 켜지도록하고, 버튼 1번은 LED가 꺼지도록 하며,

버튼 2번은 버튼을 누를때마다 켜졌다가 꺼졌다가를 반복하는 것을 구현했다.

 

/*
 * button.h
 */ 

#ifndef BUTTON_H_
#define BUTTON_H_

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

#define LED_DDR			DDRC
#define LED_PORT		PORTC
#define BUTTON_DDR		DDRD
#define BUTTON_PIN		PIND
#define BUTTON_ON		0
#define BUTTON_OFF		1
#define BUTTON_TOGGLE	2

enum
{
	PUSHED,			// 0
	RELEASED		// 1
};
enum
{
	NO_ACT,			// 0
	ACT_PUSHED,		// 1
	ACT_RELEASE		// 2
};

typedef struct _button{
	volatile uint8_t	*ddr;		// 버튼 연결 방향 설정 포트
	volatile uint8_t	*pin;		// 연결된 핀
	uint8_t				btnPin;		// 핀번호
	uint8_t				prevState;	// 버튼의 상태 플래그(깃발)
}Button;

void buttonInit(Button *button, volatile uint8_t *ddr, volatile uint8_t *pin, uint8_t pinNum);
uint8_t buttonGetState(Button *button);


#endif /* BUTTON_H_ */

 

/*
 * button.c
 */ 

#include "button.h"

void buttonInit(Button *button, volatile uint8_t *ddr, volatile uint8_t *pin, uint8_t pinNum)
{
	button->ddr = ddr;
	button->pin = pin;
	button->btnPin = pinNum;
	button->prevState = RELEASED;			// 버튼이 떨어져 있는 상태로 출발
	*button->ddr &= ~(1<<button->btnPin);	// pinNum 번호를 입력으로 방향 설정
}

uint8_t buttonGetState(Button *button)
{
	uint8_t curState = *button->pin & (1<<button->btnPin);
	
	if((curState == PUSHED) && (button->prevState == RELEASED))	// 안누른 상태에서 누르면
	{
		_delay_ms(50);		// debounce code(채터링현상 방지)
		button->prevState = PUSHED;	// 이전상태를 누른 상태로 변화
		return ACT_PUSHED;			// 눌럿다고 반환
	}
	else if((curState != PUSHED) && (button->prevState == PUSHED)) // 누른 상태에서 떼면
	{
		_delay_ms(50);
		button->prevState = RELEASED;	// 이전상태를 뗀 상태로 변화
		return ACT_RELEASE;				// 떼었다고 반환
	}
	return NO_ACT;						// 아무것도 안할 때...
}

 

/*
 * Button_1.c
 */ 

#include "button.h"

int main(void)
{
	LED_DDR = 0xff;	// LED 출력방향 설정
	Button btnOn;
	Button btnOff;
	Button btnToggle;
	
	buttonInit(&btnOn, &BUTTON_DDR, &BUTTON_PIN, BUTTON_ON);
	buttonInit(&btnOff, &BUTTON_DDR, &BUTTON_PIN, BUTTON_OFF);
	buttonInit(&btnToggle, &BUTTON_DDR, &BUTTON_PIN, BUTTON_TOGGLE);
	
	while (1)
	{
		if (buttonGetState(&btnOn) == ACT_RELEASE)
		{
			LED_PORT = 0xff;
		}
		if (buttonGetState(&btnOff) == ACT_RELEASE)
		{
			LED_PORT = 0x00;
		}
		if (buttonGetState(&btnToggle) == ACT_RELEASE)
		{
			LED_PORT ^= 0xff;
		}
	}
}

 

Button_toggle 결과

LED_div, Button, Button_toggle 끝!