CS/마이크로프로세서

[마이크로프로세서] ATmega 128 디지털 입출력 제어 (2)

nowkoes 2023. 10. 30. 19:30

본문

LED와 스위치 제어 이론

J-Kit-128-1의 회로도 일부

 

 지난 시간에 배운 입출력 포트를 레지스터로 제어하여 LED를 점등해 보자. 일단 J-KIT-128-1의 회로도를 살펴보면, LED가 PORTA와 연결되어 있음을 알 수 있다.  또한 LED가 LED의 양극이 MCU와 연결되어 있다는 것을 확인할 수 있다.

 

 

 

 이는 Current Source 방식을 채용하고 있다는 것을 의미한다. 이 방식은 핀의 출력이 1일 때 LED가 점등된다. 실습을 진행하기 위해선 핀당 허용 전류를 확인하고, 저항을 선택하는 과정이 필요하나 J-KIT-128은 이미 모듈화가 되어 있어 생략해도 된다.

 

J-KIT-128-1 회로도 일부

 

 그리고 LED 점등 시 추가적인 제어를 위해 스위치를 사용한다. 스위치 1은 PORTE의 4번 핀과 연결되어 있고, 스위치 2는 PORTE의 5번 핀과 연결되어 있다는 것을 확인할 수 있다. 

 

 

 스위치의 상태가 정해지지 않은 상태플로팅(Floating) 상태라고 한다. 이 불안정한 상태를 방지하기 위해 풀업 저항 또는 풀다운 저항을 사용할 수 있다. 풀업(Pull Up) 저항그라운드를 연결하여 스위치가 닫혔을 때 0V, 열렸을 때 5V로 정의한다. 반대로 풀다운(Pull Down) 저항스위치와 전압을 연결하여 닫히면 5V, 열리면 0V로 정의한다. J-KIT-128-1의 경우, 스위치가 그라운드와 연결되어 있으므로 풀업 저항을 사용한다.

 

 

 또한 스위치를 제어할 때 주의해야 할 사항이 하나 더 있다. 바로 바운싱(Bouncing) 현상, 혹은 채터링(Chattering) 현상이다. 이는 스위치의 물리적 접점 불균일성으로 인해, 스위치의 상태 변화 시 짧은 시간 동안 전압이 불안정하게 되는 현상을 의미한다. 이러한 현상은 스위치를 한 번 눌렀을 때 여러 번의 on/off 신호를 발생시켜 오작동을 야기할 수 있다. 이를 해결하기 위한 방법으로 스위치 디바운싱(Debouncing)이 있다.

 

 디바운싱 방법은 하드웨어적 접근과 소프트웨어적 접근으로 나눌 수 있다. 하드웨어적 방법은 커패시터를 이용한 RC 회로를 구성하여 채터링 현상을 필터링하는 방식이다. 이 방법은 채터링 현상을 확실히 해결할 수 있지만, 추가적인 회로 구성 비용과 크기 증가의 단점이 있다. 반면, 소프트웨어적 방법은 스위치의 상태 변화를 감지한 후 일정 시간 동안 MCU가 아무런 동작을 하지 않게 만드는 방법이다. 이 방법은 추가 비용이나 크기 증가 없이 구현 가능하지만, 디바운싱의 시간 설정에 따라 정상 동작 감지에 문제가 발생할 수 있다.


LED와 스위치 제어 실습

 

 이번 시간에는 위에서 언급한 LED와 스위치를 제어해보는 시간을 가지도록 해보겠다. 목표는 LED를 0.2초 간격으로 왕복하는 동작을 구현하고, SW1을 누르면 LED를 오른쪽으로, SW2를 누르면 LED를 왼쪽으로 이동하게 하는 것이다. 참고로 이 실습에서 Microchip Studio라는 IDE를 사용한다.

 

비트 시프트

#define LEFT 1
#define RIGHT 0
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRA = 0xFF;
    DDRE = DDRE & ~(1 << PINE4) & ~(1 << PINE5);
    PORTA = 0x01;
    
    int direction = LEFT;

 먼저 PORTA를 LED 출력 단자로 사용하기 위해 DDRA 레지스터를 0xFF(모두 출력)으로 설정한다. 또한 SW1과 SW2를 입력으로 사용하기 위해 비트 마스크를 이용해 주었다. 그리고 방향을 상수로 정의하여 LEFT는 1을 의미하게, RIGHT는 0을 의미하게 하여 direction 변수로 제어한다.

 

    while (1)
    {
        _delay_ms(200);
		
        if(direction == LEFT)
            PORTA <<= 1;
		
        else if(direction == RIGHT)
            PORTA >>= 1;
		
        if(PORTA == 0x01 || !(PINE & (1 << PINE5)))
            direction = LEFT;
		
        else if(PORTA == 0x80 || !(PINE & (1 << PINE4)))
            direction = RIGHT;
    }

    return 0;
}

 매 반복마다 0.2초의 딜레이를 주었다. 문제에서 0.2초 왕복이라는 조건이 있었긴 하지만, 이 조건이 없더라도 적절한 시간 지연은 필요하다. 만약 이 딜레이 없이 프로그램을 동작시키면, 컴퓨터의 연산 속도를 눈이 따라가지 못해 순식간에 LED가 점등된 것으로 보일 것이다. 

 

 그리고 방향 변수의 값에 따라 왼쪽 혹은 오른쪽으로 이동하게 기본적으로 설정하고, 스위치가 눌렸을 때 각각의 조건에 맞게 방향을 바꿔주면 된다. 여기서 LED가 움직인다는 것은 비트의 입장에서 한 칸씩 왼쪽 혹은 오른쪽으로 이동하는 것이므로, 비트 시프트 연산자를 이용하였다.

 

총합본

더보기
#define LEFT 1
#define RIGHT 0
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
	DDRA = 0xFF;
	DDRE = DDRE & ~(1 << PINE4) & ~(1 << PINE5);
	PORTA = 0x01;
	int direction = LEFT; // 1: 왼쪽 -1: 오른쪽

	while (1)
	{
		_delay_ms(200);
		
		if(direction == LEFT)
			PORTA <<= 1;
		
		else if(direction == RIGHT)
			PORTA >>= 1;
		
		if(PORTA == 0x01 || !(PINE & (1 << PINE5)))
			direction = LEFT;
		
		else if(PORTA == 0x80 || !(PINE & (1 << PINE4)))
			direction = RIGHT;
	}

	return 0;
}

 

패턴 테이블

#define LEFT 1
#define RIGHT 0
#define COUNT_MAX 8
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRA = 0xFF;
    DDRE = DDRE & ~(1 << PINE4) & ~(1 << PINE5);
    PORTA = 0x01;
    
    unsigned char leftPatternTable = {0x01, 0x03, 0x08, 0x0F, 0x10, 0x30, 0x80, 0xF0};
    unsigned char rightPatternTable = {0xF0, 0x80, 0x30, 0x10, 0x0F, 0x08, 0x03, 0x01};
    int direction = LEFT; // 1: 왼쪽 -1: 오른쪽
    int count = 0;

    while (1)
    {
        
        if(direction == LEFT)
            PORTA = leftPatternTable[cnt++];
            
        else if(direction == RIGHT)
            PORTA = rightPatternTable[cnt++];
              	
        if(PORTA == 0x01 || !(PINE & (1 << PINE5)))
            direction = LEFT;
		
        else if(PORTA == 0x80 || !(PINE & (1 << PINE4)))
            direction = RIGHT;
	
    	if(count == COUNT_MAX)
            count = 0;
        
        _delay_ms(200);
    }

    return 0;
}

 만약 비트 시프트 연산자 대신 다른 방법을 이용하고 싶다면, LED의 이동 패턴을 배열에 기록해 인덱싱하는 패턴 테이블을 활용해도 좋다.

 

실제 동작 영상


요약본

디지털 입출력 제어
1. LED 제어
 a. Current Sink: 핀의 음극이 MCU와 연결되어, 출력이 0일 때 점등되는 방식
 b. Current Source: 핀의 양극이 MCU와 연결되어, 출력이 1일 때 점등되는 방식

2. 스위치 제어
 a. 플로팅 상태: 상태가 정해지지 않아 불안정한 상태
  ① 풀업 저항: 그라운드와 스위치가 연결되어, 스위치를 누를 때 0V, 땔 때 5V를 입력하는 저항
  ② 풀다운 저항: 전압원과 스위치가 연결되어, 스위치를 누를 때 5V, 땔 대 0V를 입력하는 저항
 b. 채터링 현상: 스위치의 접점이 불균일해 입/출력이 떨리는 현상
  ① 하드웨어적 해결: RC 회로를 이용해 LPF 설계
  ② 소프트웨어적 해결: 딜레이
반응형