[31호] 너무 쉬운 아두이노 DIY ③ – 시한폭탄 카운트다운 계수기와 디지털 전압계
너무 쉬운 아두이노 DIY ③ 시한폭탄 카운트다운 계수기와 디지털 전압계
글 | 신상석 ssshin@jcnet.co.kr
이 강의는 아두이노를 가지고 간단하게 생활에 필요한 용품을 만들어 보는 강의입니다. 뚝딱뚝딱 뭔가 자신만의 DIY 용품을 만들어 보는 쏠쏠한 재미가 있는 강의라고나 할까요? 이미 주변에 아두이노와 관련한 많은 책이 출간되었고 카페나 블로그를 통하여 강의가 진행된 경우도 꽤나 많이 있는데도 불구하고, 이 지면을 통하여 강의를 개설한 이유는 다음과 같습니다.
1. 아두이노의 초보자들을 위한 쉽고 재미있는 강의가 거의 없는 것 같아, 가능하면 초등학생(?)까지도 함께 해 볼 수 있는 그런 강의를 한 번 해보고 싶어서…
2. 아두이노를 가지고 뭔가 조금은 다른, 자신만의 창의적인(?) DIY를 할 수 있는 자리를 만들어주고 싶어서…
3. 디바이스마트매거진은 임베디드와 관련된 독자들이 많고, 발행 부수도 많아, 저와 제가 속한 회사(제이씨넷) 그리고 임베디드홀릭 카페의 홍보에 도움이 될 것 같아서…
현재 구상하고 있는 회차별 내용을 간략하게 정리해 보면 다음과 같습니다. (변경될 수 있습니 다.)
회차 | 내용 |
1 | 3색 신호등 만들기 |
2 | 카멜레온 반지, 스위치를 이용한 신호등 게임기 |
3 | FND로 만드는 디지털전압계, 카운트다운 계수기 |
4 | 어두워지면 켜지는 정원등, 공중회전그네 |
5 | 캐롤송 카드, 컬러링 온도계 |
6 | 스마트폰으로 조정하는 스마트카 |
앞으로 즐겁고 알찬 강의가 될 수 있도록 최선을 다할 것을 약속 드리며, 이 강의를 보는 독자들도 메일이나 카페를 통하여 Q&A(Question & Answer)나 피드백을 주셔서, 함께 정감을 나눌 수 있는 계기가 되기를 기대해 봅니다.
여러분, 안녕하세요. 항상 즐거운 강사 신상석입니다.
이번 회에서는 숫자를 디스플레이 할 수 FND를 가지고 만들 수 있는 2가지 DIY 작품을 만들어 볼까 합니다. 하나는 007 시리즈와 같은 액션 영화에 자주 나오는 [시한폭탄 카운트다운 계수기]이고, 다른 하나는 건전지 등의 전압을 측정할 수 있는 [디지털 전압계]입니다. 두가지 모두 일상생활 속에서 여러가지 형태로 응용이 가능한 것이어서 이번 기회에 다루는 기술을 잘 습득하여 다른 DIY 작품을 만드는데 많은 도움이 되시기 바랍니다.
그럼, 출발해 볼까요? GO~ GO~
■ FND (Flexible Numeric Display)
FND는 ‘Flexible Numeric Display’의 약어로, 직역하면 ‘숫자를 자유롭게 디스플레이 할 수 있는 부품’이 되겠습니다. 다들 아시지요? 7-segement(세븐 세그먼트, 도트를 빼면 숫자 표시 부분은 7개의 LED로 이루어졌기 때문에 이렇게 불러짐)라고 주로 불렀는데 요즘은 FND라고도 많이 부르고 있습니다. 보통 요렇게 생겼지요.
1개짜리도 있고, 2개, 3개, 4개, … n개 짜리도 있습니다. 디지털시계나 디지털체중계 등에는 액정 형태로도 많이 사용되고, 증권사 객장에서는 주가를 나타내는 전광판 형태로도 사용됩니다. 엘리베이터의 층수를 나타내는 디스플레이로도 이것이죠..
자, 그러면 FND의 정체는 무엇일까요? 1개 숫자를 나타내는 FND의 숫자 부분을 조금 자세히 들여다 보면, 디스플레이 숫자판이 8조각(도트(점) 포함)으로 구분되어 있는 것을 알 수 있습니다.
뭔가 생각나는 것이 없나요?
음~ 혹시… 이것도 LED의 일종?
예, 맞습니다. 짐작하신 대로입니다. FND 숫자판의 각 조각은 하나의 LED로 구성되어 있습니다. 즉, 형태가 각각 다른 LED 8개가 숫자 형태를 유지하는 위치에 1개씩 놓여져 있는 것이 FND인 것이지요. 처음에 우리가 다루었던 LED를 8개 준비하여 아래 그림과 같이 위치이동과 형태/크기 변경의 과정을 거치면 FND 1개가 되는 것입니다.
■ FND 구조와 연결 방법
기본적인 구조는 알았으므로, 일단 숫자 1개를 표시할 수 있는 FND를 조금 더 세밀하게 들여다 보겠습니다. 아래 그림을 보시지요.
1개짜리 FND에는 보통 신호(다리)가 10개 할당되어 있는데 이 신호 중 A, B, C, …, G 그리고 DP 까지 8개의 알파벳 이름이 할당되어 각각 LED 1개씩 연결되어 있고, 나머지 2개는 모두 GND(왼쪽 FND) 또는 모두 VCC(오른쪽 FND)에 연결되어 있습니다.
또한, GND 신호가 할당된 FND는 각 LED의 캐소드(cathode)에 해당되는 부분이 공통으로 묶여서 GND에 연결되어 있고, VCC 신호가 할당된 FND는 각 LED의 애노드(anode)에 해당되는 부분이 공통으로 묶여서 VCC에 연결되어 있다는 것을 알 수 있습니다. 이 때 전자를 공통 캐소드 타입(Common Cathod Type)이라고 하는데, 각 알파벳 신호 8개는 아두이노의 디지털포트 핀에 연결하고 GND로 표시된 신호는 아두이노의 GND핀에 연결하면 이 FND를 제어할 수 있습니다. 마찬가지로 후자는 공통 애노드 타입(Common Anode Type)이라고 하는데, 이것도 전자와 비슷한 형태이지만 GND 대신 VCC로 표시된 신호가 있어 이곳은 아두이노의 VCC(+5V) 핀에 연결하는 점만 다릅니다. 한가지 조심하여야 할 것은 알파벳으로 표시된 신호를 아두이노의 핀과 연결할 때는 LED 연결 시와 마찬가지로 반드시 직렬저항(보통 220~1K 오옴)을 연결한 후에 아두이노 핀에 연결하여야만 과전류에 의한 고장을 방지할 수 있다는 점입니다.
자, 연결 방법을 알았으니, 일단 연결을 해볼까요? 먼저 우리가 사용할 FND를 보겠습니다.
FND의 이름은 [SMA42046]로. 구글에서 이것을 찾아보면 데이터시트가 나오는데 아래 그림과 같이 정보가 나옵니다. 우리는 Common Cathode 타입을 사용합니다. FND는 현재 자신이 가지고 있는 것이나 구입하기 쉬운 것을 아무거나 선택하여 사용하면 됩니다.
핀 번호도 보이고, A~DP까지 신호가 할당된 것이 보이므로 바로 연결할 수 있을 것 같습니다.
보통 FND의 점(dot)이 있는 쪽 핀 배열의 맨 왼쪽, 또는 이름이 적혀있는 면의 맨 왼쪽이 1번 핀입니다만, FND마다 핀 배치가 다를 수 있으므로 각자 사용하는 부품의 데이터시트를 잘 보고 적용하시기 바랍니다.
신호 A~DP까지 8개의 신호는 순서대로 아두이노의 D2~D9에 할당하는 것으로 하겠습니다. FND의 신호와 아두이노 신호 사이에는 220오옴~1K오옴의 저항을 넣어주는 것을 꼭 잊지 마시고 이제 아두이노와 연결해 보겠습니다.
예쁘게 보이려고 나름 애썼는데, 괜찮은가요?
■ FND로 숫자 표현하기
자, 그럼 이제 공통 캐소드 방식의 FND를 기준으로 1개의 숫자를 표현하는 방법을 살펴보겠습니다. 기분이 좋아지도록 Lucky Seven (‘7’)을 한 번 표현해보도록 하지요. (GND 신호는 아두이노 GND핀에 연결된 상태라고 가정합니다. ‘7’이 디스플레이로 보이려면 아래와 같이 노란색 불이 들어와야 하므로, A, B, C 신호가 ON(High, 1) 되어야 합니다. 나머지 신호는 당연히 OFF(Low, 0) 되어야 하겠지요.
즉, A=1, B=1, C=1, D=0,, E=0, F=0, G=0, DP=0 이 되도록 각 신호에 연결된 아두이노의 핀을 이 상태로 만들면 되겠습니다. 지난번에 배운 digitalWrite( ) 함수를 이용하면 되겠네요. 간단하지요? 그럼 바로 프로그램을 해보겠습니다.
void setup()
{
pinMode(2, OUTPUT); // pin2 ~ pin9 까지 모두 출력
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
}
void loop()
{
// FND에 ‘7’ 이라고 보여지려면 {A, B, C, D, E, F, G, DP} = {1, 1, 1, 0, 0, 0, 0, 0} 이어야 함
digitalWrite(2, HIGH); // A(2번핀) = 1
digitalWrite(3, HIGH); // B(3번핀) = 1
digitalWrite(4, HIGH); // C(4번핀) = 1
digitalWrite(5, LOW); // D(5번핀) = 0
digitalWrite(6, LOW); // E(6번핀) = 0
digitalWrite(7, LOW); // F(7번핀) = 0
digitalWrite(8, LOW); // g(8번핀) = 0
digitalWrite(9, LOW); // DP(9번핀) = 0
}
바로 실행해 봅니다.
FND에 Lucky Seven ‘7’ 숫자가 디스플레이 되었나요? 너무 쉽지요?
그러면 이번에는 숫자 ‘0’을 혼자 힘으로 만들어보겠습니다. 3분 드립니다. 3~, 2~, 1~
모두 다 잘 되었으리라 믿고, 이번에는 조금 더 효율적인 다른 방법을 사용하여 ‘0’ 이라는 숫자를 만들어 보도록 하지요. 숫자 표현은 어레이를 이용하고, 반복에 대한 부분은 for 문을 이용하면 매우 효율적인 프로그램이 됩니다..
#define FND_A 2 // 핀 매치, A는 2번핀
#define FND_DP 9 // 핀 매치, DP는 9번핀
void setup()
{
int i;
for (i=FND_A; i<=FND_DP; i++)// 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // pin2 ~ pin9 까지 모두 출력으로 세팅
}
void loop()
{
boolean digit[8] = {1, 1, 1, 1, 1, 1, 0, 0}; // ‘0’ : A=1, B=1, C=1, D=1, E=1, F=1, G=0. DP=0 // 각 핀 값을 이렇게 어레이로 처리하면 매우 편리
int i;
for (i=FND_A; i<= FND_DP; i++)
digitalWrite(i, digit[i-2]); // 위 핀에 대응되는 1, 0 값을 Write
// i는 2부터 시작이므로 digit[i-2]로 하여야만
// digit[0]부터 시작하게 됨에 주의!!!
}
뭐, 앞에서의 예와 똑같은 것이긴 하지만 for 반복문을 넣으니까 프로그램이 뭔가 조금 더 고급스러워 보입니다.
한 단계 더 전진해 보겠습니다.
0, 1, 2, … 9 까지의 숫자가 1초마다 순서대로 변하는 초시계 프로그램을 작성해 봅시다.
위의 예처럼 각 숫자를 표현하기 위한 핀 값을 모두 정의해 놓은 후 이것을 1초마다 돌아가면서 디스플레이하면 되겠네요. 8개 어레이를 갖는 10개의 숫자가 필요하므로, 이것을 2차원 어레이로 표현하면 10 x 8 어레이가 되겠고, 이것들을 1초마다 돌아가면서 값을 바꾸어주어야 하므로 for 문을 이용하여 반복 처리하면 될 것 같습니다. 음. 조금 어려울 수 있지만, 찬찬히 이해해 보도록 하지요.
아래 프로그램을 보겠습니다.
#define FND_A 2 // 핀 매치, A는 2번핀
#define FND_DP 9 // 핀 매치, DP는 9번핀
void setup()
{
int i;
for (i=FND_A; i<=FND_DP; i++) // 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // pin2 ~ pin9 까지 모두 출력으로 세팅
}
void loop()
{
boolean digit[10][8] = { {1, 1, 1, 1, 1, 1, 0, 0}, // 숫자 ‘0’에 대응
{0, 1, 1, 0, 0, 0, 0, 0}, // 숫자 ‘1’에 대응
{1, 1, 0, 1, 1, 0, 1, 0}, // 숫자 ‘2’에 대응
{1, 1, 1, 1, 0, 0, 1, 0}, // 숫자 ‘3’에 대응
{0, 1, 1, 0, 0, 1, 1, 0}, // 숫자 ‘4’에 대응
{1, 0, 1, 1, 0, 1, 1, 0}, // 숫자 ‘5’에 대응
{1, 0, 1, 1, 1, 1, 1, 0}, // 숫자 ‘6’에 대응
{1, 1, 1, 0, 0, 0, 0, 1}, // 숫자 ‘7’에 대응
{1, 1, 1, 1, 1, 1, 1, 1}, // 숫자 ‘8’에 대응
{1, 1, 1, 0, 0, 1, 1, 0,} }; // 숫자 ‘9’에 대응
int i, j;
for (j=0; j<=9; j++) // 숫자가 0~9까지 10개이므로 10번 루프
{
for (i=FND_A; i<= FND_DP; i++) // 정해진 한 개의 숫자 디스플레이
{
digitalWrite(i, digit[j][i-2]); // 각 핀에 대응되는 1, 0 값을 Write
}
delay(1000); // 한 숫자가 디스플레이 되는 시간인 1초 대기
}
}
2차원 어레이를 쓰는 방법과 중복 for 루프를 쓰는 방법은 데이터가 많을 때 처리하는 방법으로 매우 유용하니 조금 복잡하고 어렵지만 사용법을 잘 익혀놓는 것이 좋겠습니다.
자, 이제 업로드하고 잘 나오나 봅시다. 성공? 예, 당근 성공이지요. 축하합니다.
■ 4-digit FND
한 개의 숫자를 표현하는 방법은 알았으므로 이번에는 여러 개의 숫자를 디스플레이할 수 있는 4 FND(여기서는 4-digit FND)에 대하여 살펴보겠습니다. 4-digit FND는 모양상으로는 1-digit FND,를 4개 합한 것과 같습니다. 그럼 뭐 특별히 4개짜리를 공부할 필요가 있겠는가 생각할 수도 있겠지만, 공부할 필요가 있습니다. 왜냐하면 연결방법이나 프로그램 방법이 약간 다르기 때문입니다.
위에서 살펴본 1-digit FND,를 생각해보면 이것과 연결된 신호선은 데이터 신호선 8개(A~DP)와 VCC 또는 GND선 2개이므로 총 10개입니다. 단순히 1-digit FND를 4개 묶어서 4-digit FND를 만들었다고 가정하면 VCC와 GND는 공통이고 신호선이 아니니까 빼 놓으면 4-digit FND에 필요한 신호선의 개수는 8 X 4 = 32 가 되어 총 32개의 신호선이 필요합니다. 그런데, 우리의 아두이노 UNO는 디지털 신호선이 D0~D13까지 14개, 아날로그 신호선이 A0~A5까지 6개이므로 이것을 몽땅 끌어다가 쓴다고 해도 최대 20개밖에 제공하지 못하므로 문제가 생기게 되는 것이지요.
그래서 FND를 만든 사람들이 이 문제를 어떻게 해결할 수 있을까 생각하다가… “그렇지! ‘잔상효과’를 이용하면 되겠구나!”하고 아래와 같이 연결하는 방법을 발견하였습니다. (믿거나 말거나…)
우리가 사용할 공통 캐소드 타입 4-digit FND인 [WCN4-0036-SR-C11]의 데이터시트를 보면서 설명해 보겠습니다.
위 연결도를 잘 보면 1-digit FND에서 사용하였던 신호가 4-digit FND에 동일한 신호로 중복되어 연결되어 있는 것을 알 수 있습니다. 그리고 GND로 표시되었던 공통캐소드가 COM으로 표시되어 있고 이것은 따로 4개의 신호로 분리되어 할당되어 있네요. 이것이 무슨 의미인고 하니, A~DP까지의 신호는 4개의 FND가 공통으로 사용하고, 분리된 COM 신호를 이용하여 4개의 FND 중 한 개만 활성화하여 사용하겠다는 뜻입니다. 예를 들어 가장 왼쪽의 COM 신호만 0으로 하고 나머지를 1로 만들면 A~DP까지의 신호는 0이건 1이건 오른쪽 3개의 FND는 불이 들어오지 않으니까요(COM=1이므로 다이오드(LED)가 절대로 도통되지 않음).
즉, 왼쪽 FND 하나만 사용할 수 있는 상황이 됩니다. 이런식으로 COM의 값을 조정하면 선택된 FND만 불을 켤 수 있는 상황을 만들 수 있습니다. 결과적으로 이렇게 연결하면 데이터 신호선 8개와 FND 선택 신호선 4개만 아두이노에서 제어하여 어떤 순간에 한 개의 FND만 제어할 수 있게 됩니다. 즉, 아래와 같이 어떤 한 순간에 한 개의 숫자는 디스플레이가 가능한 것이지요.
이제 어떻게 4개의 숫자가 모두 디스플레이될 수 있는지에 대하여 생각해 보지요.
잔상효과라는 것은 방금 전에 눈으로 본 것을 뇌가 기억하고 있어서 그 다음에 본 것과 방금 전에 본 것이 겹쳐져 보이는 현상을 말하는데 예를 들어 영화나 애니메인션에서 1초에 여러 장(약 30장 정도)의 사진(그림)을 연속으로 보여주면 이것이 따로 따로 독립된 사진(그림)으로 느껴지는 것이 아니라 자연스럽게 움직이는 동영상처럼 보이게 되는 것을 말합니다.
위 4개의 FND 디스플레이를 굉장히 짧은 시간 동안 연속적으로 디스플레이하게 되면 (1초에 30번 이상 디스플레이 되는 정도의 속도) 사람은 잔상효과에 의하여 위 FND에 디스플레이된 형태를 아래와 같이 느끼게 되는 것입니다.
아하, 그러면 표현하고 싶은 글자를 돌아가면서 디스플레이 하되, 1초에 30번 이상 반복하여 디스플레이되도록 하면 해결이 되는 것이네요. 예. 맞습니다.
■ 시한폭탄 카운트다운 계수기
4-digit FND에 숫자를 표현하는 방법을 알았으므로 이제 드디어 우리가 만들고 싶은 물건을 DIY해 볼 시간입니다. 첫번째 DIY는 전쟁 영화, 액션 영화에 자주 나타나는 아래와 같이 생긴 [시한폭탄 카운트다운 계수기]로 하지요.
생긴게 무서워서 어떻게 해야 할 지 아무런 생각이 나지 않는다구요?
하~ 그럴지도… 저도 생각이 잘 …
실감나게 한다고 이 예를 생각해 내긴 하였지만, 사실 뒤에 있는 폭탄을 제거하고 나면 그냥 정해진 숫자가 하나씩 줄어드는 카운트다운 계수기입니다. 조금 어려울 수는 있지만 당연히 4-digit FND로 구현이 가능합니다. 그러면, 무엇을 만들지 기능 규격을 먼저 정확하게 정의하고 회로 설계와 스케치 프로그램에 들어가도록 하겠습니다.
[기능 규격]
· 4-digit FND로 분과 초를 디스플레이하며, 앞의 2 digit은 분, 뒤의 두 digit은 초를 나타내고, 2번째 digit은 점(dp, dot point)를 함께 디스플레이한다.
· 1초마다 디스플레이는 1초 감소하고 00.00이 되면 멈춘다.
· 전원이 입력되면 초기값은 05.00이고 카운트다운이 시작된다.
· 스위치(SW)가 1개 있어 스위치를 한 번 누르면 카운트다운이 정지되고 한 번 더 누르면 다시 카운트다운이 진행된다. (더 누르면 앞의 동작 반복)
[연결도]
자, 이제까지 배운 내용을 가지고 FND와 SW를 아두이노에 연결해 보도록 하겠습니다. FND의 A~DP 신호는 순서대로 아두이노의 D2~D9핀에 할당하도록 하겠습니다. COM 신호 4개도 순서대로 D10~D13핀에 할당합니다. (FND의 가장 오른쪽 초 단위 digit을 결정하는 COM핀을 D10에 할당) SW는 디지털 입력이므로 디지털핀에 연결하여야 하는데 자세히 보니, D0핀 및 D1핀만 남아있네요. 여기다가 연결하는 것도 방법이긴 하나, 보통 D0핀과 D1핀은 UART(Univeral Asynchronous Receiver/Transmitter) 통신시 RX(Receive) 및 TX(Transmit) 핀으로 사용되므로 시리얼통신을 사용하게 되는 경우에 대비하여 사용하지 않고 남겨 놓는 것이 보통입니다. 그래서 우리는 A5핀을 스위치핀으로 사용하도록 하겠습니다. 어? 그런데 아날로그핀도 디지털핀으로 사용할 수 있나요? 예, 보통은 잘 사용하지 않지만 사실 A0~A5 신호는 아날로그 입력 신호뿐만 아니라 디지털 입출력 신호로도 사용이 가능합니다. 이 경우 A0~A5는 각각 D14~D19로 매핑이 되므로 프로그램 시 주의하셔야 합니다.
스위치에 풀업 저항을 달아 평상시는 ‘1’이 되도록 하고, 스위치를 누르면 ‘0’이 되도록 로직을 꾸미고 A5(D19)핀에 연결하겠습니다. 모두 연결하면 아래와 같은 형태가 되겠네요.
[알고리즘]
이제 어떻게 스케치 프로그램을 작성하여야 할지 알고리즘을 먼저 작성해 봅시다.
처음에 카운트다운할 초 단위 시간을 count 라는 변수에 넣어놓고(count = 300), 이것을 1초마다 1씩 감소시키면서 이 값에 해당되는 분, 초를 계산하고 이것이 4개의 FND로 디스플레이하면 되겠습니다. 물론 디스플레이 할 때마다 한 번씩은 스위치가 눌러졌는지도 확인하여야 하겠지요. 스위치가 눌러졌다면 정지하여야 하니까요.
이전에 배운 방법으로 간단하게 표현하여 보도록 하겠습니다. 아래와 같이 되겠네요.
자, 그럼 프로그램을 작성해 보실까요.
#define FND_A 2 // 핀 매치, A는 2번핀
#define FND_DP 9 // 핀 매치, DP는 9번핀
#define FND_COM0 10 // 핀 매치, COM0(가장 오른쪽 FND)는 10번핀
#define FND_COM3 13 // 핀 매치, COM3(가장 왼쪽 FND)는 13번핀
#define SW 19 // 핀 매치, SW는 A5(D19)핀
#define GO 0 // stopFlag의 GO 상태
int count; // 현재 카운트다운 값(초기값 = 300(초))
int stopFlag = GO; // 스위치가 눌려졌는지 판단하는 플래그
byte digit[10] = {0x3F, 0×06, 0x5B, 0x4F, 0×66, 0x6D, 0x7D, 0×27, 0x7F, 0x6F}; // 숫자 0, 1, 2, …, 9 까지의 10개에 대한 LED 디스플레이 어레이
byte fnd[4]; // byte = unsigned char
void setup()
{
int i;
for (i=FND_A; i<=FND_DP; i++)// 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // FND 데이터는 pin2 ~ 9 까지 모두 출력
for (i=FND_COM0; i<=FND_COM3; i++) // 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // FND 선택신호는 pin10 ~ 13 까지 모두 출력
pinMode(SW, INPUT); // SW 선택신호는 pin19 로 입력
count = 300; // 카운트다운 초기값, 300초=5분
}
void loop()
{
int min10, min1, sec10, sec1, i;
min10 = (count/60)/10; // 분의 10자리는 분 값을 10으로 나눈 몫 값
min1 = (count/60)%10; // 분의 1자리는 분 값을 10으로 나눈 나머지 값
sec10 = (count%60)/10; // 초의 10자리는 초 값을 10으로 나눈 몫 값
sec1 = (count%60)%10; // 초의 1자리는 초 값을 10으로 나눈 나머지 값
for (i=0; i<100; i++) // 1초 동안 현재 시간을 FND에 디스플레이 (100번 x 10ms = 1초)
{
displayFnd(min10, min1, sec10, sec1); // FND 디스플레이 함수, 1번 실행시간은 약 10ms
if (digitalRead(SW) == 0) // 스위치를 눌려졌으면
stopFlag = ~stopFlag; // STOP이면 GO, GO면 STOP으로 상태 변경, 스위치를 누를 때마다 상태가 변경(toggle)됨
}
if ((count != 0) && (stopFlag == GO))
count–; // 카운트가 0이 아니고 GO 상태이면 카운트 1 감소하고, 그렇지 않으면 카운트 정지!!!
}
void displayFnd(int val1, int val2, int val3, int val4) // FND 디스플레이 함수, 1번 실행시간은 약 100ms
{
int i, j, k, l;
fnd[3] = digit[val1]; // 10분 단위 값에 해당되는 FND 디스플레이 값
fnd[2] = digit[val2] | 0×80; // 1분 단위 값에 해당되는 FND 디스플레이 값
// 소수점이 찍히므로 0×80(dp) 을 OR 해 줌
fnd[1] = digit[val3]; // 10초 단위 값에 해당되는 FND 디스플레이 값
fnd[0] = digit[val4]; // 1초 단위 값에 해당되는 FND 디스플레이 값
for (i=0; i<=3; i++) // 각 FND에 대하여
{
for (k=0; k<=3; k++)
{
if (k==i)
digitalWrite(k+10, LOW); // 정해진 FND만 선택하여 활성화시킴
else
digitalWrite(k+10, HIGH); // 대응되지 않은 FND는 비활성화시킴
}
for (j=0; j<=7; j++) // 지정된 FND내의 각 LED에 대하여
{
if (fnd[i] & (1<<j))
digitalWrite(j+2, HIGH); // 표시하고 싶은 숫자에 대응되는 핀만 ON
else
digitalWrite(j+2, LOW); // 대응되지 않은 핀은 OFF
}
delay(2.5); // 대기 시간 = 2.5ms, loop 실행 시간 = 2.5 x 4 = 10 ms
}
}
전체적으로 프로그램이 살짝 어려운 부분이 있고 길어졌지만, 코딩마다 코멘트를 넣었으니 차근차근 보시면 이해가 되실 거라고 믿습니다.
자, 이제 실행시켜 보겠습니다.
성공일까요? 예, 당근 성공이지요. 왜냐하면 사실은 코딩 ▶ 컴파일 ▶ 업로드 ▶ 결과체크 ▶ 에러!!! ▶ … 수정 ▶ … ▶ 또 수정 ▶ … 이렇게 해서 다 잘되는 것 확인하고 올린 프로그램이니까요. 저도 한 번에 성공할 수는 없습니다.
1개를 완성하였으니, 일단 잠깐 휴식을 취하고, 잠시 후에 2번째 DIY에 도전해 보겠습니다. 모두 10분간 휴식~!
■ 아날로그 신호
두번째 DIY 작품을 만들기 위하여는 아날로그에 대한 이해가 필요하므로 이것에 대하여 간단히 먼저 알아본 후 진행하겠습니다.
아날로그(analog)는 디지털(digital)에 대응되는 말로, 연속적으로 변화하는 양을 일컫는 말입니다.
디지털이 0과 1로 대변되는 것이고, 칼로 무 자르듯이 이것 아니면 저것, 양 아니면 음, 합격 아니면 탈락과 같이 똑 떨어지는 개념을 설명하는 단어라면, 아날로그는 0.7과 같이 똑 떨어지지 않고, 이것도 아니고 저것도 아니며, 조건부 합격 같이 딱 부러지게 구분하기 어렵고 경계가 모호한 개념을 설명하는 단어지요.
그림으로 그려보면 아래와 같이 되는데… 뭔가 디지털은 인공적이고 딱딱한 느낌이, 아날로그는 자연적이고 부드러운 느낌이 난다고나 할까요?
디지털 | 아날로그 |
우리는 0, 1로 대표되는 디지털기기인 아두이노를 가지고 지금 놀고 있는데, 그렇다면 위와 같은 아날로그 값을 디지털 값을 이용하여 어떻게 표현할 수 있을지 생각해 보겠습니다?
아래 그림에서 빨강색으로 표시되어 있는 아날로그 값은 1비트의 디지털 값으로 나타낸다면 1이됩니다. 0 아니면 1로 표현해야 하니까 이렇게 되겠지요? 회색으로 표시된 범위에 있는 아날로그 값은 모두 디지털로 표현되었을 때 1이 되는 것이지요.
이것을, 2비트의 디지털 값으로 나타낸다면 어떻게 될까요? 2비트면 4개의 단계로 나눌 수 있으니까 11이 되겠네요.
계속해서, 3비트의 디지털 값으로 이것을 나타낸다면? 예. 당근 110이 되겠습니다.
눈치채셨겠지만 아날로그를 표현하는 디지털의 비트 수를 많이 사용하면 사용할수록 보다 정확하게 아날로그 값을 디지털로 표현할 수 있으며, 당연히 오차의 범위가 줄어듭니다. 하지만 디지털의 비트 수를 무한정 크게 할 수는 없으므로 적정한 선(8비트, 10비트, 12비트, 16비트, 24비트 등)에서 선택하게 됩니다.
■ 아두이노의 아날로그 입력
그러면 아두이노는 어떤 아날로그 처리 능력을 가지고 있을까요? 예, 아두이노는 A0~A5 핀으로 입력된 0~5V 사이의 아날로그 입력(전압)을 10비트로 표현할 수 있는 능력을 내장하고 있습니다. 예를 들어 0V의 전압값을 갖는 신호를 아두이노는 0000000000 값으로 표현하고 5V의 전압값을 갖는 신호는 1111111111 값으로 표현합니다. 0V~5V 사이의 입력을 0~1023까지 1024 단계의 디지털 값으로 표현하는 것입니다. 1V는 1024/5 값에 해당되므로 임의의 값 XV에 해당되는 값은 X*1024/5 의 디지털 값이 되는 것이므로, 간단히 말해서 입력된 아날로그값에 1024/5 값을 곱해주면 변환된 디지털 값을 아래와 같이 구할 수 있겠습니다.
A0~A5 핀으로 입력된 아날로그 값 | 변환된 디지털 값 (10비트) |
0 V | 1024/5 * 0 = 0 |
5 V | 1024/5 * 5 = 1024 ▶ 1023 (1023이 최대이므로 5V는 1023으로 처리) |
2.5 V | 1024/5 * 2.5 = 512 |
4.5 V | 1024/5 * 4.5 = 921.6 ▶ 921 (정수값으로만 표현되므로) |
이번에는 반대로 10비트로 변환된 디지털 값의 실제 아날로그값은 얼마인지를 구해 보겠습니다.
디지털로 표현된 1의 값이 5/1024 V를 나타내므로 Y라는 디지털 값을 가진 신호의 원래 아날로그값은 5/1024 * Y V가 되어 아래와 같이 되겠습니다.
디지털 값 | 10진수 | 아날로그 값 | 실제 아날로그 값(범위) |
0000000000 | 0 | 5/1024 * 0 = 0 | 5/1024 * 0 이상 ~ 5/1024 * 1 미만 |
1111111111 | 1023 | 5/1024 * 1023 = 4.995 |
5/1024 * 1023 이상 ~ 5/1024 * 1024 미만 (5 포함) |
1000000000 | 512 | 5/1024 * 512 = 2.5 | 5/1024 * 512 이상 ~ 5/1024 * 513 미만 |
0000000100 | 8 | 5/1024 * 8 = 0.039 | 5/1024 * 8 이상 ~ 5/1024 * 9 미만 |
■ 디지털 전압계
이제 기초 준비가 되었으니 2번째 DIY 작품을 만들어 볼 시간입니다. 이번에는 건전지나 리튬전지 등의 전압을 측정하여 FND에 디스플레이해 주는 디지털 전압계를 만들어 보겠습니다. 기능 규격부터 먼저 정확하게 정의하고 회로 설계와 스케치 프로그램으로 넘어가는 것이 좋겠네요.
[기능 규격]
· A5로 입력되는 아날로그 전압을 측정하여 이것을 4-digit FND에 소수 둘째자리까지 표현한다. 물론, 소수점도 표시한다.
· FND는 오른쪽 정렬 형태로 나타낸다. 즉, 가장 왼쪽의 FND는 사용하지 않고 2~4번째에 값을 표시한다.
· 측정 주기 즉, FND의 디스플레이 업데이트 주기는 약 0.5초로 한다.
A5의 아날로그값을 읽어서 이것을 전압값으로 소수 둘째자리까지 변환한 후 정수 부분은 왼쪽에서 2번째 FND에 표현하고(소수점 포함), 소수 첫째자리 숫자는 3번째 FND에, 소수 둘째자리는 3번째 FND에 표현하면 될 것 같습니다. 즉, 크게 2단계로 나누어서 1단계는 변환된 전압값의 각 자리에 맞는 숫자를 구하고, 2단계는 이 숫자를 FND에 디스플레이하면 되겠네요. 각 단계별로 세부적인 해결책을 찾아봅시다.
[회로 연결]
FND.의 연결은 카운트다운 계수기에서 스위치 회로만 제거한 후, 측정하고자 하는 건전지의 (+)극을 아두이노의 A5핀에, 건전지의 (-)극을 아두이노의 GND핀에 연결하면 되겠습니다. 아래와 같이 되겠네요.
[알고리즘]
준비가 되었으니 스케치 프로그램을 어떻게 작성하여야 할지 알고리즘을 작성해 보겠습니다.
우리는 디지털 값을 출력할 때 digitalWirte( ) 함수를 사용하였고, 디지털 값을 입력할 때는 digitalRead( ) 함수를 사용하였습니다. 이번에는 아날로그 값을 입력하여야 하므로… 예, 맞습니다. analogRead( ) 함수를 사용하겠습니다. 이 함수는 아날로그값을 0~1023까지의 디지털 값으로 바꿔주는 함수이며, 아래와 같은 형태를 갖습니다.
analogRead(pin)
pin : 아날로그 입력핀 번호에 해당하는 숫자
return값 : 0~1023까지의 정수
analogRead( ) 함수를 이용하여 읽어온 건전지의 전압은 0~1023까지의 값이므로 이것을 실제 아날로그 값인 0~5까지의 값인 X.YZ 형태의 숫자로 치환하여 이것을 FND에 순서대로 디스플레이하면 되겠네요 숫자가 5를 넘지 않으므로 첫번째 FND는 항상 0을 디스플레이하면 되겠죠?
FND에 디스플레이할 숫자를 구하는 방법만 조금 더 상세하게 설명하면 다음과 같습니다.
· 아날로그 값(A0)을 전압값(anal_vol)으로 바꾸는 방법
: anal_vol = A0 / 1024.0 * 5.0
· anal_vol에서 정수 부분을 추출하는 방법
: (anal_vol)을 float에서 int로 형 변환
· anal_vol에서 소수 첫째 자리를 추출하는 방법
: (anal_vol * 10)을 int로 형 변환 후 10으로 나눈 나머지를 선택
· anal_vol에서 소수 둘째 자리를 추출하는 방법
: (anal_vol * 100)을 int로 형 변환 후 10으로 나눈 나머지를 선택
FND에 디스플레이할 숫자가 정해진 상태에서 이것을 디스플레이하는 방법은 [카운트다운 계수기]를 스케치할 때 사용한 displayFND( ) 함수를 그대로 사용하면 되니 실제로 새로 프로그램할 양은 얼마 되지 않습니다.
자, 그러면 이제 모든 준비가 끝났으니 위의 사항을 종합하여 스케치 작성에 들어가 봅시다. 혼자 하실 수 있는 분은 스스로 작성해 보시구요.
#define FND_A 2 // 핀 매치, A는 2번핀
#define FND_DP 9 // 핀 매치, DP는 9번핀
#define FND_COM0 10 // 핀 매치, COM0(가장 오른쪽 FND)는 10번핀
#define FND_COM3 13 // 핀 매치, COM3(가장 오른쪽 FND)는 13번핀
#define VOLTAGE 5 // 핀 매치, VOLTAGE는 A5핀
byte digit[10] = {0x3F, 0×06, 0x5B, 0x4F, 0×66, 0x6D, 0x7D, 0×27, 0x7F, 0x6F}; // 숫자 0, 1, 2, …, 9 까지의 10개에 대한 LED 디스플레이 어레이
byte fnd[4]; // byte = unsigned char
void setup()
{
int i;
for (i=FND_A; i<=FND_DP; i++) // 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // FND 데이터는 pin2 ~ 9 까지 모두 출력
for (i=FND_COM0; i<=FND_COM3; i++) // 핀 번호가 연속적으로 할당되어 있으므로 가능
pinMode(i, OUTPUT); // FND 선택신호는 pin10 ~ 13 까지 모두 출력
}
void loop()
{
int anal_val, vol_int, vol_pri1, vol_pri2, i;
float voltage;
anal_val = analogRead(VOLTAGE); // 입력된 아날로그 측정 값(0~1023)
voltage = anal_val / 1024.0 * 5.0; // 변환된 전압 값 (0.0 ~ 4.99)
vol_int = (int)(voltage); // 전압값의 정수 부분 숫자 (0~4), 형변환
vol_pri1 = (int)(voltage * 10) % 10; // 전압값의 소수 첫째자리 숫자 (0~9), 형변환
vol_pri2 = (int)(voltage * 100) % 10; // 전압값의 소수 둘째자리 숫자 (0~9), 형변환
for (i=0; i<50; i++) // 0.5초 동안 현재 시간을 FND에 디스플레이 (50번 x 10ms = 1초)
displayFnd(0, vol_int, vol_pri1, vol_pri2); // FND의 가장 왼쪽값은 0으로 디스플레이하고, 오른쪽 3개 값은 측정값을 디스플레이 (숫자 전달)
}
void displayFnd(int val1, int val2, int val3, int val4) // FND 디스플레이 함수, 1번 실행시간은 약 100ms
{
int i, j, k, l;
fnd[3] = digit[val1]; // 10자리 정수 값에 해당되는 FND 디스플레이 값
fnd[2] = digit[val2] | 0×80; // 1자리 정수 값에 해당되는 FND 디스플레이 값
// 소수점이 찍히므로 0×80(dp) 을 OR 해 줌
fnd[1] = digit[val3]; // 소수 첫째자리 값에 해당되는 FND 디스플레이 값
fnd[0] = digit[val4]; // 소수 둘째자리 값에 해당되는 FND 디스플레이 값
for (i=0; i<=3; i++) // 각 FND에 대하여
{
for (k=0; k<=3; k++)
{
if (k==i)
digitalWrite(k+10, LOW); // 정해진 FND만 선택하여 활성화시킴
else
digitalWrite(k+10, HIGH); // 대응되지 않은 FND는 비활성화시킴
}
for (j=0; j<=7; j++) // 지정된 FND내의 각 LED에 대하여
{
if (fnd[i] & (1<<j))
digitalWrite(j+2, HIGH); // 표시하고 싶은 숫자에 대응되는 핀만 ON
else
digitalWrite(j+2, LOW); // 대응되지 않은 핀은 OFF
}
delay(2.5); // 총 대기 시간 = 2.5ms, loop 실행 시간 = 2.5 x 4 = 10 ms
}
}
아주 간단하지요? 이제 업로드해서 결과를 확인해 보겠습니다.
결과 확인!
조금 사용한 AA 건전지 1개를 측정한 값이 1.4V 근처값이 나오므로 제대로 측정이 되는 것 같습니다. 정확하게 동작하는지를 확인하기 위하여 A5 핀을 VCC(+5V)에 연결하여 5.00 또는 4.99 값이 나타나고, GND 핀에 연결하여 0.00 또는 0.01 정도의 값이 나타나는지도 확인하면 더욱 완벽한 확인이 되겠지요. 제대로 나오는 것을 보니 이번 것은 누워서 떡먹기로 성공한 것 같습니다.
뭐, 살다보면 어려운 날도 있고 쉬운 날도 있고… 흥망성쇠(興亡盛衰)… 새옹지마(塞翁之馬)…
자, 오늘은 여기까지입니다. FND를 이용한 DIY는 전체적으로 연결선도 조금 복잡하고 프로그램도 조금 어려웠던 것 같은데… 함께한 여러분이 수고 많이 하셨습니다. 모두 다 피곤한 듯하니 오늘은 군소리 말고 빨리 사라져야겠네요. 그럼 다음 강의 때까지 모두 안녕~~~~ 휘리릭~~~
{
int sec10, sec1, ms100, ms10, i;
sec10 = (count/100)/10; // 분의 10자리는 분 값을 10으로 나눈 몫 값
sec1 = (count/100)%10; // 분의 1자리는 분 값을 10으로 나눈 나머지 값
ms100 = (count%100)/10; // 초의 10자리는 초 값을 10으로 나눈 몫 값
ms10 = (count%100)%10; // 초의 1자리는 초 값을 10으로 나눈 나머지 값
displayFnd(sec10, sec1, ms100, ms10); // FND 디스플레이 함수, 1번 실행시간은 약 10ms(1/100초)
if (count == 10000) // 99.99 다음은 00.00
count = 0;
else
count++; // 1/100초 증가
}
· loop( ) 함수 외의 코딩은 [디지털 전압계] 스케치와 동일하므로 생략!
· 다른 회로 연결은 [디지털 전압계] 회로 연결과 동일하고, 스케치는 그대로 사용