시프트 레지스터로 다수의 LED 깜빡이기

 
[컴퓨터월드] 아두이노는 이탈리어로 ‘친한 친구’라는 뜻을 가진 대표적인 오픈소스 하드웨어다. 딱딱하고 접근하기 힘들었던 임베디드 분야를 누구나 쉽게 접근할 수 있도록 만든 미니 기판이라 할 수 있다. 이번 강좌에 사용하는 아두이노 오렌지보드는 한국형 아두이노라 할 수 있다.

아두이노의 보급은 오픈소스 하드웨어의 확산을 불러일으켰고 메이커 문화의 확산에도 큰 기여를 했다. 최근에는 인텔, 마이크로소프트 등 대형 기업들도 이런 오픈소스 하드웨어시장에 뛰어들기 시작했다. 그만큼 오픈소스 하드웨어 시장의 잠재력이 커졌다는 얘기다.


시프트 레지스터란 무엇인가?

플립플롭(flip-flop) 또는 래치(latch)라고도 하는, 0과 1만을 갖는 1비트(bit) 값을 저장할 수 있는 기본 회로가 있다. 레지스터란 이런 플립플롭을 일렬로 여러 개 연결해, 여러 비트로 구성한 2진수를 저장할 수 있게 한 것을 말한다.

▲ 8비트 시프트 레지스터

이번호에서 사용하는 8비트 시프트 레지스터는 8개의 플립플롭이 연결돼 있으며, 단방향 또는 양방향으로 매 클럭 펄스마다 한 플립플롭씩 저장된 값을 좌우로 이동시킬 수 있는 레지스터다. 클럭 펄스란 일정한 간격을 갖는 전기적 펄스로, 회로 내의 동작을 동기화하는 데 사용된다.

대표적인 시프트 레지스터의 용도는 데이터의 직렬-병렬변환(SIPO, Serial In Parallel Out)으로, 직렬 입력으로 다중 출력을 만들어 낼 수 있다. 이 예제에서는 오렌지보드의 핀 3개를 사용해 8개의 LED를 제어해 보도록 한다.


시프트 레지스터 사용방법

시프트 레지스터는 핀마다 고유의 기능이 있으므로 연결 시 방향에 주의해야 한다. 홈이 파진 부분의 좌측이 1번 핀이 되며, 총 16개의 핀으로 구성돼 있다.

시프트 레지스터가 동작하는 과정을 살펴보면

1. 한 번에 8개의 클럭 펄스를 받는다. 이 때 시프트 레지스터는 클럭 펄스가 들어올 때마다 동작하게 된다.
2. 데이터 입력 핀이 HIGH일 경우 1이 저장되고, LOW일 경우 0이 저장된다.
3. 8개의 펄스가 수신이 완료되면 래치(Latch)를 활성화시켜 Latch register에 복사한다.


필요한 부품 목록

 

하드웨어 연결하기

브레드보드

1. 오렌지보드의 5V 핀을 브레드보드의 +버스에 연결한다.
2. 오렌지보드의 GND 핀을 브레드보드의 -버스에 연결한다.
3. 시프트 레지스터를 그림과 같이 홈이 위로 가게 해, 양쪽 IC 영역에 걸치도록 꽂는다.
4. 캐소드(짧은 단자)가 반대쪽의 -버스에 연결되도록 LED 8개를 꽂는다.
5. 각 LED의 애노드(긴 단자)에 330Ω 저항을 연결한다.
6. 시프트 레지스터의 15, 1, 2, 3, 4, 5, 6, 7번 핀을 8개의 LED 애노드에 각각 연결한다.
7. 시프트 레지스터의 8번 핀과 13번 핀을 -버스에 연결한다.
8. 시프트 레지스터의 10번 핀과 16번 핀을 +버스에 연결한다.
9. 시프트 레지스터의 12번 핀을 오렌지보드 2번 핀에 연결한다.
10. 시프트 레지스터의 11번 핀을 오렌지보드 3번 핀에 연결한다.
11. 시프트 레지스터의 14번 핀을 오렌지보드 4번 핀에 연결한다.
12. 8개의 LED 캐소드가 연결된 –버스를 오렌지보드 GND와 연결된 –버스와 연결한다.

 
 


전자 회로도

 
 

소스코드 

/*
제목 : 시프트 레지스터로 다수의 LED 깜빡이기
내용 : 시프트 레지스터로 8개의 LED를 한 번에 깜빡이게 해봅니다.
*/

// 시프트 레지스터의 STcp(latch)를2번번으로 설정합니다.
// 클럭 입력이 활성화되면, 입력을 받아 저장하고, 클럭 입력이 비활성화되면 출력 측으로 전달합니다.
// 출력은 클록이 다시 활성화될 때까지 그 값을 유지할 것입니다.
int latch = 2;
// 시프트 레지스터의 SHcp(clock)를 3번 핀으로 설정합니다.
// 클럭펄스를 발생시켜 데이터 값이 레지스터에 저장되도록 합니다.
int clock = 3;
// 시프트 레지스터의 Ds(data)를 4번 핀으로 설정합니다.
// 이 데이터 핀을 통해 시프트 레지스터에 원하는 동작을 수행하게 합니다.
int data = 4;

// 실행 시 가장 먼저 호출되는 함수이며, 최초 1회만 실행됩니다.
// 변수를 선언하거나 초기화를 위한 코드를 포함합니다.
void setup() {
// latch가 연결된 핀을 OUTPUT으로 설정합니다.
pinMode(latch, OUTPUT);
// clock가 연결된 핀을 OUTPUT으로 설정합니다.
pinMode(clock, OUTPUT);
// data가 연결된 핀을 OUTPUT으로 설정합니다.
pinMode(data, OUTPUT);
}

// setup() 함수가 호출된 이후, loop() 함수가 호출되며,
// 블록 안의 코드가 무한히 반복 실행됩니다.
void loop() {
//8개의 LED를 순차적으로 켜고 끕니다.
for (int i = 1; i <= 8; i++) {
// latch를 비활성화해 데이터를 입력받도록 설정합니다.
digitalWrite(latch, LOW);
// 데이터로 입력된 값을 클럭 펄스로 동기화해 시프트 레지스터에 저장합니다.
shiftOut(data, clock, MSBFIRST, i);
// latch를 활성화해 저장된 입력 데이터를 출력 핀으로 전달합니다.
digitalWrite(latch, HIGH);
// 1초간 대기합니다.
delay(1000);
}
}

코드를 통해 작동하는 시프트 레지스터를 간단하게 설명하면, 매 클럭마다 특정 값을 data에 전달하는 것이다.

// 데이터로 입력된 값을 클럭 펄스로 동기화해 시프트 레지스터에 저장합니다.
shiftOut(data, clock, MSBFIRST, i);

위의 shiftOut을 사용하면 클럭에 따라 data의 값이 1씩 증가한다. 이는 for문을 통해 i값이 증가함에 따라 data의 값도 같이 증가하게 되기 때문인데, 증가하는 값을 이진수로 표현하게 되면 아래와 같다.

0000 0001 → 0000 0011 → 0000 0111 → 0000 1111 → 0001 1111 → 0011 1111 → 0111 1111 → 1111 1111 …

그런데 위와 같이 값이 1로 증가한다고 해서 LED가 켜지지는 않는다. 증가시킨 data는 Latch에 HIGH 신호가 들어가기 전까지는 가둬진 상태이기 때문에, Latch에 HIGH를 전달해야 비로소 LED에 불이 켜지게 된다.

// latch를 활성화해 저장된 입력 데이터를 출력 핀으로 전달합니다.
digitalWrite(latch, HIGH);

Latch에 HIGH 신호가 들어가면 LED에 불이 켜지고, 그렇게 LED 8개에 불이 전부 다 들어오면 다시 처음 1개부터 시작해 반복적으로 불이 켜지게 된다.


마치며

보통 일반 유저들은 아두이노 UNO 사이즈의 핀도 다 쓰기 힘들지만, LED를 주렁주렁 달아야 할 경우나 LCD와 같이 여러 센서를 쓰는 특별한 경우에는 아두이노의 핀이 모자랄 수도 있다.

▲ 4×4×4 LED Cube

위의 LED cube 같은 경우가 시프트 레지스터를 사용하는 대표적인 예다. LED 64개를 한꺼번에 제어하기 위해서는 64개의 핀이 필요한데, 아두이노 입장에서는 턱없이 많은 숫자다. 이런 상황에서 쓰일 수 있는 부품이 바로 시프트 레지스터다. 보통 상황이라면 8개의 핀을 써야 하는데, 시프트 레지스터를 사용하면 3개의 핀을 이용해 8개의 핀을 사용할 수 있기 때문에 5개의 핀을 절약할 수 있다.

그러나 이는 위와 같은 특정 프로젝트에서 많이 쓰이는 부품이므로, 일반적인 상황에서는 많이 쓰이지 않는다. 다만 사용하게 되면 여러 개의 핀을 각각 제어할 필요 없이 몇 개의 핀만 제어하면 돼 편리하다는 장점이 있다.

위 튜토리얼에 대한 글은 http://kocoafab.cc에서도 볼 수 있다.

저작권자 © 컴퓨터월드 무단전재 및 재배포 금지