[Arduino/아두이노] 3색 LED 켜기!

 

안녕하세요! 여러분!

오랜만에 다시 아두이노 포스팅으로 돌아왔습니다!

 

저번시간까지 아두이노를 컴퓨터와 연결하고, 예제 프로젝트를 실행해보고(블링크(Blink)), 외부라이브러리까지 추가해보았는데요!

 

오늘은 대망의 아두이노 외에 다른 '부품'(혹은 '소자' 라고도 하죠?)을 써보는 프로젝트를 해보도록 하겠습니다! 와~

 

대망의 첫 부품은 아주아주 간단한 3색 LED입니다!

 

그냥 LED도 아니고, 왜 3색이냐구요..?

 

어차피 그냥 LED와 3색 LED가 큰 차이가 없기 때문입니다. 그냥 R/G/B, 레드/그린/블루 LED 3개를 쓰나, 3색 LED 하나를 쓰나!

 

그럼 이제 아주 간단한 3색 LED를 만들어 보기 위해 차근차근 알아가 보도록 하겠습니다!

항상 이제부터는 아두이노와 물리적인 부품들을 연결할 것이기 때문에, 부품의 스펙을 알아보고 아두이노와 어떻게 연결(배선)할지를 알아본 뒤, 코딩을 통하여 프로젝트를 완성할 것입니다.

 

그럼 일단 3색 LED 부품의 스펙을 먼저 알아보고 가겠습니다!

 

1. 3색 LED 스펙-

제가 사용하는 3색 LED 부품은 HW-479이지만, 다른 부품을 사용해도 상관이 없는게, 이 3색 LED는 무조건 4개의 핀으로 이루어져 있으며, 하나는 -로 표기되어있는 GND, 나머지 세 핀이 각 각 R, G, B를 입력받는 핀입니다.

각각, 앞면과 뒷면 이미지인데요! 각 핀이 어떤건지 너무 잘 나와있죠? 여기서 우리는 -에 아두이노 GND를, RGB에 각각 해당하는 핀을 연결할 겁니다!

 

그리고 오늘은 대망의 첫 부품을 가지고 진행하는 프로젝트인 만큼, 아두이노와 부품의 연결에 제일 중요한 두가지를 알아보려고 합니다.

바로 케이블(선)브레드보드(빵판) 입니다.

 

1. 케이블

케이블은 말 그대로 부품과 아두이노를 연결는 선 입니다.!

아두이노는 좋은 점이 '점퍼 케이블'이라고하는, 케이블 양 끝에 점퍼가 있는 케이블로 쉽게 연결을 할 수 있다는 점입니다.

따로 납땜을 하지 않아도 된다는 것이지요.

케이블에는 두가지 종류가 있습니다.

F-M(암-수) 케이블, 그리고 M-M(수-수) 케이블입니다.

F-M 케이블
M-M 케이블

 

2. 브레드보드(빵판)

브레드보드의 경우 크기는 매우 다양할 수 있습니다. 보통이 30 홀(hole)짜리 브레드보드를 쓰게되구요, 그거보다 작은 브레드보드도 있고, 저처럼 60 홀 짜리도 있고, 이보다 더 큰 것도 있습니다. 여기는 구멍이 나있어서 부품을 막 꽂을 수 있도록 되어있는데 여기서도 규칙이 있습니다.

자 위의 그림처럼, ABCDE끼리는 세로로 서로 연결되어있습니다.(가로로는 서로 끊어져 있습니다)[빨간선]

FGHIJ끼리도 세로로 연결되어있습니다.(가로로는 서로 끊어져 있습니다)[빨간선]

ABCDE와 FGHIJ는 끊어져있습니다.

제일 위에는 전원 선입니다. GND(-)[파란색] 라인과 전원(+)[빨간색] 라인이죠.

전원 선의 경우 가로로 쭉 이어져 있습니다. 당연히 +와 -간은 서로 끊어져 있구요.[주황선]

60홀의 경우 30홀을 기준으로 좌우가 끊어져있습니다.

지금은 개념이 어렵더라도 한번 읽고 머리에 기억해두면 이후에 소자를 연결하거나 할때 '왜 이렇게 꽂는지'에 대해서 이해하기 쉬워지니 그냥 가볍게 읽고 넘어가 봅시다! 간단하게 말하자면 저런 규칙이 있어서 브레드 보드를 쓸 수 있습니다.

참고로 브레드보드의 어원은 말 그대로 초기 비전문가들이 빵(브레드) 자르는 판(보드)에다가 회로를 구성했기에 브레드보드로 이름 붙었다네요! 빵판이라고도하죠!

 

* 그리고 여기서 아두이노와 부품을 연결하는 방식 두가지!

하나는 F-M케이블(암-수케이블)로 아두이노와 직접 연결!

또하나는 M-M케이블(수-수케이블)로 빵판(브레드보드)를 이용하여 연결!

이렇게 두가지 방법이 있습니다.

현재 3색 LED에는 내부적으로 내장 저항이 있어서 아두이노와 직접 F-M케이블로 연결해도 되지만, 보통 소자들은 내부적으로 저항이 없기 때문에, 브레드보드(빵판)에 부품을 꽂고, 저항을 꽂고, 거기에 M-M케이블로 연결하는 것이 보통입니다.

그렇기에 오늘은 브레드보드를 이용하여 아두이노와 소자를 케이블로 연결해보겠습니다.

 

2. 아두이노와 연결(배선)

배선 첫 시간이니 배선에 대해서 말씀드리자면, 가능한한 색을 다르게 써서 보기 편하게, 헛갈리지 않게 하는것이 중요합니다!

우선 전원 선(+선)은 빨간색, 접지 선(-선, GND선)은 까만색이 기본이고, 그 외에는 구분이 잘 가고 직관적인 색이면 됩니다!

오늘 저희는 전원 선이 따로 없기 때문에 RGB에 각각 빨간색, 초록색, 파란색 선을 쓸 것이고 GND에 검은색 선을 쓸 것입니다.

3색 LED이기 때문에 각 R, G, B에 입력신호를 줄 pin이 하나씩 있어야 겠죠? 그러니 일단 선 세개를 쓸 것이구요, 입력할때 전기를 넣어 줬으면 이 전기가 빠져나갈 곳이 있어야 하니 GND pin도 하나 있어야 하겠습니다. 결국 3색 LED는 선 4개로 아두이노와 연결하여 쓸 수 있습니다.

한번 부품도로 그려보면

이와 같이 되겠습니다. 별건 아니고요, 이 그림에서는 3색 LED 핀 순서가 R - B G 이기때문에 이런 식의 배선이 되었습니다만, 실제 부품에서는 부품 핀 순서가 - R G B 순서이므로 이렇게 꽂아주시면 됩니다.

아두이노에 연결할때는 아두이노 우노 기준으로 9번핀에 R, 10번핀에 B, 11번핀에 G를 할당해 주었고, GND(-)는 아두이노 GND핀에 바로 연결하였습니다.

실제로 부품을 꽂아봅시다!

'기기나 기판에 장치나 부품 따위를 실제로 사용할 수 있도록 설치'하는 것을 '실장하다'라고 하는데요, 실제로 부품을 실장해보면 위의 이미지와 같을 것입니다.

R, G, B, - 3색 LED 핀에 각각 9, 10, 11, GND를 연결해 주었습니다. 이로써 아두이노와 부품간 배선이 끝났습니다!

 

3. 코딩

저는 개인적으로 이 영역을 가장 좋아합니다. 이미 연결된 상태에서 코딩으로 이 부품을 자유자재로 다룰 수 있기 때문이죠!

자 일단 기본적인 세팅을 해 봅시다!

이전까지 저희가 열심히 설치했던 아두이노 IDE를 켜주세요!

처음 켜면

이렇게 setup 구간(함수)와 loop 구간(함수)로 구성되어 있는 것을 볼 수 있습니다.

아두이노의 가장 핵심적인 부분인데요, 아두이노는 처음 프로그램이 실행되면서 setup 함수에 있는 내용을 한번 쭉 실행한 뒤에 그뒤로는 loop 함수만 반복적으로 실행합니다. 즉, setup에는 말 그대로 이 프로그램에 대한 초기 셋팅들을 해주고 loop에서 아두이노가 계속적으로 처리할 내용을 적어주는거죠!

결국 아두이노는 setup -> loop -> loop -> loop ... 이렇게 실행하게 됩니다.

그럼 먼저 setup 영역부터 코딩해볼까요?

일단 아두이노는 각 핀에 대해서 정의를 내려줘야합니다.

'이 핀은 내가 전원을 내보낼 핀이야', '이 핀은 내가 전원을 읽을 핀이야' 이런식으로요!

이 정의를 해주는 함수는 pinMode()입니다. 이 함수는 값이 두개가 필요합니다. 핀 넘버와 핀 모드죠.

핀 넘버는 말그대로 저희가 사용할 pin입니다. 아두이노에 숫자로 써져있는거죠. 오늘 저희가 사용할 핀은 9, 10, 11번 핀입니다.

핀 모드는 내가 출력할거면 OUTPUT, 내가 읽을거면 INPUT을 쓰면 됩니다.

자, 이제 바로 코딩해보도록하겠습니다. 전원선과 GND선은 따로 핀모드 설정이 필요없기 때문에 RGB에 대응하는 9,10,11번 핀만 핀모드를 설정하겠습니다.

void setup(){
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
}

 

자, 그럼 이제 loop함수로 가볼까요!

가장간단하게 1초동안 빨간 불을 켜고, 바로 다음 1초동안 초록 불을 켜고, 바로 다음 1초동안 파란불을 켜보도록하겠습니다.

 

void loop(){
  digitalWrite(11, LOW);
  digitalWrite(9, HIGH);
  delay(1000);
  digitalWrite(9, LOW);
  digitalWrite(10, HIGH);
  delay(1000);
  digitalWrite(10, LOW);
  digitalWrite(11, HIGH);
  delay(1000);
}

 

여기서 digitalWrite는 0, 1 즉 켜고 끄는 걸로 해당 핀에 신호를 주겠다는 명령어이구요, delay는 ms단위로 쉬겠다는 의미입니다!

결국 완성된 코드는 다음과 같습니다!

void setup(){
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
}

void loop(){
  digitalWrite(11, LOW);
  digitalWrite(9, HIGH);
  delay(1000);
  digitalWrite(9, LOW);
  digitalWrite(10, HIGH);
  delay(1000);
  digitalWrite(10, LOW);
  digitalWrite(11, HIGH);
  delay(1000);
}

 

4. 결과

자, 그럼 배선과 코딩한 것을 바탕으로 결과를 한번 볼까요!?

자, 잘 돌아가는 것을 확인했습니다!

지금 현 상황에서 배선도 완료되어있고, 작동도 제대로 되고 있는 것을 확인했으니, 이제 코딩으로 다양한 활용이 가능해집니다!

더욱 자세한건 다음번에 알아보아요!

아참, 우리가 지금 한 것은 켜고 끄기만 하는 digitalWrite였는데요, 여기서 analogWrite는 0~255사이의 값을 PWM 방식으로 차등적으로 적용할 수 있습니다. 즉, '강도 조절'이 가능해진다는 말인데요!

이를 바탕으로 각 불이 점점 밝게 들어왔다가 꺼지는 것도 가능하답니다!

힌트는 여기까지! 다음번에 또 보아요~

delay함수 없이 LED 깜빡이기

 

가끔씩 우리는 아두이노에서 병렬처리(한번에 두 가지 이상의 작업을 동시에 수행하는 것)가 필요할 때가 있습니다.
아두이노의 loop는 사실상 ms단위로 작동하기 때문에 구문을 나열해도 거의 동시에 처리되는 것처럼 보이긴 합니다.
그렇기에 평상시에는 크게 병렬처리에 대해서 생각할 필요가 없는데요.
그러나 LED핀을 깜빡이는데 가장 간편하게 사용하는 delay()함수가 들어가게 되면, 여기서부터 골치가 아파집니다.
왜냐하면 delay()함수를 사용하게 되면 아두이노가 프로그램을 일시 중지되기 때문이죠.
따라서 delay()함수가 작동하게 되면 지정한 시간만큼 프로그램이 일시 중지되고, 그 사이에는 다른 구문을 작동할 수가 없습니다.
예를들어 delay()함수 작동 중 버튼 눌림 체크가 불가능 하다거나, 다른 명령어 작동이 불가능하죠.

오늘 올릴 포스팅은 delay()함수를 사용하지 않고 LED를 깜박이는 방법에 대해 작성할 겁니다.

간단하게 개요부터 말씀드리자면,
1) delay()를 사용하지 않고
2) LED를 켜고(끄고) 시간(ms 단위)을 기록하고(millis() 함수)
3) 이 시간이 원하는 시간이 지났는지 확인하고
4) 시간이 지났으면 LED를 끕(켭)니다.

이렇게되면, delay()로 아두이노를 일시중지 시키지 않고 loop()함수가 살아서 계속 반복적인 작업이 이루어지므로 LED가 깜빡거리는 동안 다른 작업이 가능해집니다.

쉬운 예시로 전자레인지에 피자데우기를 들 수 있습니다.
자, 전자레인지에 피자를 넣고 전자레인지를 작동시킵니다.
여기서
1) delay()함수로 10초간 기다리라고 하는 말의 의미는 전자레인지 앞에서 10초가 다 지나가기를 가만히 기다리라는 의미입니다. 결국 그동안에 다른 일은 못하죠!
2) 그러나 우리가 만약에 두가지 일을 하고 싶다면 어떻게 하나요? 전자레인지에는 시간 카운터가 달려있기에 전자레인지를 돌리고 짧은시간 다른 용무를 보고와서 전자레인지 시계를 확인하고, 10초가 지났는지 확인하고, 만약 10초가 지났으면 꺼내서 먹으면 됩니다!

1. 배선

우리는 아주 간단하게 실험해 볼 것이므로 내장된 Blink예제를 사용해보도록하죠!(즉, 따로 배선이 필요 없습니다)
Blink예제는 >>링크<< 포스팅에서 다루었습니다. 링크의 포스팅을 따라 블링크 예제를 불러와봅시다!

2. 코딩

블링크 예제의 코드는 아래와 같습니다.(상단의 주석부분은 제거하였습니다.)

// 보드의 파워나 리셋 버튼을 눌렀을 때 이 setup 함수는 한번만 작동합니다.
void setup() {
  // digital pin인 LED_BUILTIN을 output으로 초기화 합니다.
  pinMode(LED_BUILTIN, OUTPUT);
}

// 이 loop 함수는 계속 계속 영원히 반복하여 작동합니다.
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);  // LED 켜기 (HIGH는 전압 레벨)
  delay(1000);                      // 1초간 기다리기
  digitalWrite(LED_BUILTIN, LOW);   // 전압을 LOW로 설정하여 LED 끄기
  delay(1000);                      // 1초간 기다리기
}


자, 여기서 delay(1000) 즉, 1초간 아무것도 안하고 아두이노한테 기다리라고 하는 부분을 바꿔봅시다!

위의 예시에서 전자레인지의 시간만 중간중간 확인 가능하다면, 굳이 전자레인지 앞에서 지켜보고 있을 필요가 없다고했죠?
그러니 시간을 확인할 수 있는 함수를 알아봅시다!

millis()라고 하는 함수는 아두이노가 실행된 시점부터 내부적으로 카운팅하고 있는 밀리초의 시간을 반환하는 함수입니다.
즉, 이 millis()를 쓰면 중간중간 시간확인이 가능하다는 말이죠!

프로그램의 골자를 생각해봅시다.
1) 전자레인지를 돌리고 시간을 본다 => LED를 켜고 millis()로 시간을 확인한다(변수에 저장한다)
2) 시간을 확인한다 => 현재 시간이 처음 LED를 켠 시간에 비해 얼마나 지났는지 확인한다(현재시간 - LED 켠 시간)
3) 정해진 시간이 지났으면 피자를 꺼내먹는다 => [(현재시간 - LED 켠 시간) > 정해진 시간]이면 LED를 끈다

간단하죠? loop함수 내부 로직을 아래와 같이 짜봅시다.

// 1)
unsigned long startMillis;

digitalWrite(LED_BUILTIN, HIGH);
startMillis = millis();
// 2)&3)
if(millis()-startMillis >= 1000){
  startMillis = millis();
  digitalWrite(LED_BUILTIN, LOW);
}



자, 모든 조건을 만족하게 프로그래밍을 이렇게 하면... 작동하지 않습니다!
왤까요?
loop함수는 아두이노가 작동하는한 끊임없이 반복된다고 했습니다.
그런데 이 loop함수 내부에서 시작한 시간을 변수에 저장시키면, 매 loop함수가 작동할 때마다 변수에 새로운 값이 저장되기에 정확히 시작한 시간을 알 수 없죠!
그러면 대안은? 가장간단한건 loop함수 밖에서 시작시간을 설정해주거나, loop내에서도 일정한 조건일 때만 변수에 값을 넣어주는 겁니다.
게다가 loop함수 내부에서 digitalWrite(LED_BUILTIN, HIGH) 구문이 있기때문에 사실 LED_BUILTIN은 계속 켜져있을 겁니다.
일단은 가장 간단하게 갑시다! loop함수 밖에서 시작시간과 LED 켜기를 설정해줍시다.

unsigned long startMillis = millis(); // 전역변수는 함수 밖에 있어야 합니다.

void setup() {
  // digital pin인 LED_BUILTIN을 output으로 초기화 합니다.
  pinMode(LED_BUILTIN, OUTPUT);
  // 1)
  digitalWrite(LED_BUILTIN, HIGH);
}

// 이 loop 함수는 계속 계속 영원히 반복하여 작동합니다.
void loop() {
  if(millis()-startMillis >= 1000){
    startMillis = millis();
    digitalWrite(LED_BUILTIN, LOW);
  }
}



자, 이러면 작동합니다!
그러나 딱 한번만 작동합니다!
왤까요?
말그대로
1] 시작하면서 LED켜고 시간측정해!
2] 타이머가 1초가 지나가면 꺼!
끝이기 때문이죠. 1초가 지나면 꺼진상태가 그대로 유지됩니다.
그러면 어떻게 이 친구를 깜빡거리게(토글) 할 수 있을까요?
가장 간단한건 '1초일땐 꺼, 2초일땐 켜, 3초일땐 꺼...'지만, 이러면 모든 시간을 다 기록해줘야하죠..?
조금 더 머리를 써 봅시다.
지금 현재 LED 상태를 기억했다가 1초가 됐을 때 현재 LED상태를 보고 반대상태로 만들어주면 매 초마다 코딩을 하지 않아도 되지 않을까요!?
바로 실험해봅시다!

// 전역변수는 함수 밖에 있어야 합니다.
bool ledState; // led상태를 알 수 있는 변수를 하나 만듭니다. bool은 int와 다르게 참/거짓의 딱 두가지 값만 가질 수 있습니다. 우리는 결국 led가 켜졌냐/꺼졌냐 만 사용할 것이므로 bool을 사용합니다.
unsigned long startMillis = millis(); // 그리고 led 상태를 기록한 시간을 기록합시다.

void setup() {
  // digital pin인 LED_BUILTIN을 output으로 초기화 합니다.
  pinMode(LED_BUILTIN, OUTPUT);
  // 1)
  ledState = HIGH; // 그리고 바로 led를 켜지 말고, '상태'를 입력해주죠. 현재 led는 '켜짐상태'입니다.
}

// 이 loop 함수는 계속 계속 영원히 반복하여 작동합니다.
void loop() {
  if(millis()-startMillis >= 1000){ // led상태를 기록한 시간(startMillis)와 현재 시간(millis())를 비교해서 1초(1000ms)가 지났는지 판단합니다. 지났으면 if문 안으로! 만약 1초가 지나지 않았으면 이 if문은 통과합니다!
    if(ledState == HIGH){ // 현재 led상태를 확인합니다. 현재 led가 '켜짐상태'이면 '꺼짐상태'로, '꺼짐상태'이면 '켜짐상태'로 바꿔줄 겁니다.
      ledState = LOW; // 현재 led 상태가 '켜짐상태'였기에 '꺼짐상태'로 바꿔줍니다.
    }
    else{ // 아니면! 즉, 현재 led상태가 '꺼짐상태' 면
      ledState = HIGH; // 현재 led 상태를 '켜짐상태'로 바꿔줍니다.
    }
    startMillis = millis(); // 그리고 상태를 바꿔줬으니 바로 상태를 기록한 시간을 다시 기록합니다.
  }

  digitalWrite(LED_BUILTIN, ledState); // 마지막으로 '상태'를 LED에 진짜로 반영해줍니다!
}



자, 이 구문은 제대로 잘 작동합니다! 1초마다 LED_BUILTIN이 켜졌다 꺼졌다 하는군요!
전체적으로 작동로직을 한번 살펴볼까요?
처음 시작하면 setup함수 내부에서 LED를 '켜짐상태'로 설정하고, 그 시간을 기록합니다.
그리고 loop함수로 넘어오죠.
loop함수에서는 시작한시간과 현재시간을 비교하는데, 시작하고 1초도 지나지 않았습니다.
따라서 첫 if문은 바로 통과!
그 이후 loop함수 마지막에 digitalWrite로 LED_BUILTIN에 실제적으로 '상태'를 반영해줍니다.
결국 LED는 켜지고, 1초뒤에 첫번째 if문 안으로 들어갑니다.
첫번째 if문 안에서 현재 led상태가 HIGH이므로 두번째 if문으로 들어가 ledState를 LOW로 바꿔주고, 상태를 바꾼 시간을 다시 기록하고 if문을 빠져나와 digitalWrite로 LED_BUILTIN에 실제적으로 '상태'를 반영해줍니다.
그리고 이 반복이죠!

3. 더 나아가기

사실 위의 프로그램(스케치)은 loop함수 작동시마다 계속 digitalWrite를 부릅니다. 즉, 1초마다 한번씩만 바꿔주면 되는걸, 1초가 되기 전에 계속 '켜 켜 켜 켜 켜 켜 켜 켜'하고 반복신호를 주는거죠.(그리고 사실 이게 병렬처리가 되는 이유기도 합니다. 시간 체크하는 동안 계속해서 신호를 주는거죠.)
1초마다 신호를 주고, 더 짧은 코드를 공개합니다.
이번엔 주석을 지울테니 보고 더 생각해보세요!

bool ledState = LOW;
unsigned long startMillis = millis();

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if(millis()-startMillis >= 1000){
    digitalWrite(LED_BUILTIN, ledState=!ledState);
    startMillis = millis();
  }
}

AVR, ATmega128, ATmega 128, LED, ATmel Studio, ATmel Studio 7, 7Segment, 아두이노, 전자전기, FND


ATmega128로 FND 만들고 활용하기!



안녕하십니까?


하루 지나고 다시 글을 올리게 되었습니다.


그 전까지는 사실 배선도그렇고, 개념도 그렇고, 프로그래밍도 그렇고 사실 별로 그렇게 많이 어렵지 않아서 그냥 무작정 따라하기 3단계(배선! 프로그래밍! 실행!)이렇게 스파르타하게 달려왔다면 이번 것은 저도 굉장히 애를 먹었던 부분이라 조금 자세하게 설명하겠습니다.


그 전과는 뭔가 포스팅 분위기부터가 많이 달라졌죠? [몰아서 올리지 않아서 그렇습니다.]


오늘 해 볼것은 FND다루기 입니다.

FND는 Finite Numeric Display의 약자로써 7-Segment(이전 포스팅을 참조 해 주세요!)를 4개 붙여 놓은 것과 같은 형상입니다.


사실 FND와 7-Segment는 서로 교차되서 부르기도 하는데, 1자리 7-Segment를 FND로 부르기도 합니다. (사실 Finite Numeric이니까 길이 제한만 있으면 어떤 Display에 다 써도 될 것 같지만요...)


먼저 준비사항에 대하여 알아보겠습니다.


0] 준비

1) AVR

너무 당연. 항상 ATmega128을 사용하고 있습니다.

2) USB허브

깜빡하고 전 포스팅에서 언급하지 않았었는데, 따로 콘센트-플러그가 있는 USB 허브 제품(그냥 USB하나 달랑 컴퓨터에 꽂는 것이 아니라 거기에 추가로 220V 플러그가 추가로 달려있는)이 혹여 제품에 과도한 전류가 흘렀을 때도 컴퓨터에 영향을 주지 않는다고 합니다.

3) FND

이번에 사용할 모델은 HSN-5643AS-H 입니다. 4자리 7-Segment입니다.

핀은 아래와 같습니다.

여기서 D1, D2, D3, D4는 각 자리를 표시해주는 핀이며, A~G까지의 핀은 각 11, 7, 4, 2, 1, 10, 5에 매치됩니다. 3번 핀은 DP(Dot Point)입니다.

특히 이 모델은 공통 캐소드(CC) 방식으로 각 핀에 주어진 source전류가 한번에 Dx핀으로 빠져나갑니다.

즉, 8개 다 켜지면 sink 전류가 ATmega가 최대로 받아들일 수 있는 전류 100mA 보다 커지므로 필수적으로 저항을 설치해 줘야 합니다. 역기서는 A~DP까지 하나하나 저항을 달아주는 방법과, 각 Drain port에 큰 저항을 4개 써주는 방법이 있겠습니다. 저는 드레인 포트에 큰 저항을 4개 써 주었습니다.

보시면 아시겠지만 A~DP에 해당하는 핀이 1개씩밖에 없으므로 이 4개가 연속된 FND는 순차적으로 불을 켜서 표시하게 됩니다. 즉, 눈의 잔상효과로 인해 사실은 네개가 동시에 켜져있는 것이 아니지만, 인간은 한번에 다 켜져있다고 생각하게 되는 것이죠.

4) 10K 저항 *4

10K 저항을 Drain Port에 달아주었습니다. 너무 어두우면 1K 저항까지도 사용이 가능합니다.


항상 이론과 실제의 오차가 존재하듯이, 실수로 저항 없이 바로 연결이 된 적이 생겼었는데, 다행히도 어떤 부품도 상하지는 않았습니다만... 조심하시는 것이 좋을 것 같습니다.


1] 이 부품의 핀을 보고 배선을 해줍니다.

저번 7Segment와 동일하게 A~DP까지는 PORTA0~7에 연결하였습니다. 그리고 D1~4는 각 PORTG0~3에 연결하였습니다.

각 D1~D4까지는 저항을 달아주었습니다. 브레드보드(빵판)에서는 세로줄이 전부 연결이므로 핀 꽂을때 저항을 달면 옆으로 조금씩밀어주시면 되겠습니다. 같은 라인에 연결하면 전류가 저항 없이 다이렉트로 FND핀에 꽂힙니다. (이렇게 해서 저는 실수했었습니다.)


배선은 그다지 어렵지 않습니다. 그저 시간과 노가다가 오래 걸릴뿐...


2] 프로그래밍!!!!!!

대망의 프로그래밍입니다.

D1~D4는 전류가 빠져나가는 (-)입니다. 다시말해, PORTG0가 1이면 D1에서 전류를 못 흐르게 막는 다는 것이고, 이 말은 D1을 끈다는 소리와 같습니다. 그러면 PORTG0가 0이면 sink 포트가 되면서 전류가 흘러 해당하는 자리에 불이 들어오게 됩니다.

소스는 아래와 같습니다.


#include <avr/io.h>

#include <util/delay.h>

#define F_CPU 16000000UL


int d2b[10] = {0b00111111,

0b00000110,

0b01011011,

0b01001111,

0b01100110,

0b01101101,

0b01111101,

0b00100111,

0b01111111,

0b01101111};


int main(void)

{

    DDRA = 0xFF;

    DDRG = 0b00001111;

    int dp = 0b10000000;

    int flag = 0b00000000;

while(1){

for (int c=3 ; c<13 ; c++){

for (int k=0 ; k<50 ; k++){

for (int i=0 ; i<4 ; i++){

PORTA = d2b[(c-i)%10]|(dp&flag);

flag = ~flag;

PORTG = ~(0b1<<i);

_delay_ms(100);

}

}

flag = ~flag;

}

}

return 0;

}


저번 7segment에서는 각 핀이 (-)역할을 하였으므로 위에서 표시된 0b00111111을 비트 반전시켜서 포트에 출력했을 시 0이 나왔지만, 이번에는 각 핀이 (+) 단자이니 이 코드를 그대로 쓰겠습니다. d2b[0]~d2b[9]는 각 0~9를 나타냅니다.


A포트는 모든 핀을 출력핀으로 사용할 것이니 DDRA = 0xFF;로 모두 개방해줍니다.

G포트는 0~3 핀만 출력핀으로 사용할 것이니 DDRA = 0b00001111;로 첫 4개만 개방해줍니다. 여기서 PORT로 5V를 줬다 뺐다 하는 식으로 조작할 건데 이 부분을 DDR반전 시키고 PIN으로 처리해도 될 것은 같습니다만 제가 해보지 않아서 단정지을 수는 없을 것 같습니다.


dp와 flag는 저번 포스팅에서도 말씀드렸다시피 dp의 깜빡거림을 제어하기 위한 장치였기 때문에 dp와 flag를 모두 빼시고 보시면 더 소스가 간결해집니다. 여유가 되시는 분들은 한번 저 두 변수의 역할에 대해 생각해 보시는 것도 좋은 유흥이 될 것이라 생각합니다.


반복문은 가장 안쪽에서 부터 보는 것이 로직 이해해 도움이 됩니다.

가장 안쪽을 보면 4번 돌아가는 for문이 있는데, 이 것은 대충 눈치 채셨다시피 각 자릿수에 해당하는 것입니다. PORTG를 순서대로 개방하면서 각 PORTG에 해당하는 숫자를 PORTA로 지정해주는 것입니다. 저는 왼쪽으로 밀 것이기 때문에 10개 자릿 수 내에서 돌아가도록 10을 나눠주어 빙글빙글 돌렸습니다.

출력은 D4부터 D1순서로 돌아오므로 D4에 3이 나오게 하기 위해서 c변수를 3에서 출발시켰구요, 자릿수가 오름에 따라서 숫자를 하나씩 줄이기 위해서 c-i를 썼습니다.

_delay_ms()함수를 조절해보면 눈에서 이 문자가 덜 깜빡거리면서 동시에 켜져있는 것처럼 보이게 되는 지점이 있습니다. 대략 125면 어느정도 깜빡이는 수준 안에서 동시에 출력되는 것으로 보입니다. 저는 100이 가장 괜찮았습니다.

그 밖에서 50번 도는 for는 글자를 약 1초간 지속적으로 표시해주는 역할입니다. 왼쪽으로 미는 것처럼 보일 것이기 때문에 숫자가 바뀌는 부분과 표시하는 부분은 따로 진행이 되어야 합니다.

대망의 가장 바깥쪽 for문은 숫자를 증가시켜주는 역할입니다. 가장 오른쪽 숫자를 1씩 증가시키는데, 이렇게 디스플레이하면 왠지 숫자가 옆으로 진행하는 것 같죠.


이렇게 준비가 끝났습니다.

AVR에서 실행시켜보죠!


3] 실행!


자 이렇게 어려운 FND도 마무리 해 보았습니다.


다음에 다시 찾아뵙겠습니다.

#AVR, #ATmega 128, #ATmega128, #LED, #ATmel Studio 7, #아두이노, #전자전기, #ATmel Studio


ATmega128로 연속 LED 시퀀스 만들기!



8개 LED가 연속적으로 변화합니다. 코드를 수정할경우 다른 경우도 만들 수 있습니다.

1) 배선한다!

7-segment보다 덜 복잡합니다. 그냥 PA0부터 1번 LED, PA2-2번LED, ... 순으로 입력해주시면 됩니다. 복잡해 보일 것 같아서 저항은 뺐습니다. (LED가 버틸 수 있는 전압이라 뺐습니다)


2) 프로그래밍한다!

#include <avr/io.h>

#include <util/delay.h>


#define F_CPU 16000000UL


int main(void)

{

int i;

DDRA = 0xFF;

while(1){

for (i=0 ; i<8 ; i++){

PORTA = (0b00000001<<i)-1;

_delay_ms(500);

}

for (i=0 ; i<8 ; i++){

PORTA = (0b11111111<<i);

_delay_ms(500);

}

PORTA = 0x0;

_delay_ms(500);

for (i=0 ; i<8 ; i++){

PORTA = (0b10000000>>i)|PORTA;

_delay_ms(500);

}

for (i=0 ; i<8 ; i++){

PORTA = (0b11111111>>i);

_delay_ms(500);

}

}

return 0;

}

이번 프로그래밍에서 정확하게 1초를 계산할 수 있는 방법이 나왔습니다.

util/delay.h를 인클루드 해 주시고 F_CPU를 16000000UL로 define해주세요. 여기서 16000000은 눈치챈 분도 계시겠지만, ATmega128의 구동 Hz입니다. 기본은 10000000UL로 맞추어져 있으니 다시 define해주시는게 정확하게 1초가 맞게됩니다.

빌드시 warning뜨는데, 재정의 warning이니 무시해주셔도 됩니다.


저는 영상과 같은 모양을 구현하기 위해서 위와같은 코드를 짰습니다.

더 간소화 할 수 있을 것 같은 느낌적인 느낌이 들지만 그냥 적당한 선에서 타협하기로 했습니다.


3) 실행한다!


개인적으로 조금 만족할만한 결과가 나왔습니다.


다음에 다시 뵙겠습니다.

#AVR #ATmega128 #ATmega 128 #LED #ATmel Studio 7 #7 segment #7segment #아두이노 #전자전기


ATmega128로 7-segment LED 조작하기!


애석하게도 따로 찍은 사진이 없어 영상 속 화면으로 대체합니다.



1) 배선한다!

사진 속 7-segment LED는 5611BH 모델입니다. 이 모델은 공통 전원부가 (-)가 아닌 (+)입니다. 즉, 아래 이미지에서 8번 핀에 ATmega 그림에서 아래쪽에서 제일 왼쪽에 있는 VEXT 포트를 연결해주어야 합니다.

저는 A부터 차례대로 PA0와 대응시켰습니다. 즉, A-PA0, B-PA1식으로 DP-PA7로 하였습니다.


2) 프로그래밍 한다!

소스코드입니다.

#include <avr/io.h>


int d2b[10] = {0b10111111,

0b10000110,

0b11011011,

0b11001111,

0b11100110,

0b11101101,

0b11111101,

0b10100111,

0b11111111,

0b11101111};


void delay_secp10(int sec){

volatile int i, j, k;

for(k=0;k<sec;k++){

for (i=0;i<100;i++){

for(j=0;j<1000;j++);

}

}

}


int main(void)

{

    DDRA = 0xFF;

    int dp = 0b10000000;

    int flag = 0b00000000;

while(1){

for (int i=9 ; i>-1 ; i--){

PORTA = ~(d2b[i])|(dp&flag);

delay_secp10(5);

flag = ~flag;

PORTA = ~(d2b[i])|(dp&flag);

delay_secp10(5);

flag = ~flag;

}

}

return 0;

}

d2b배열은 인덱스 넘버가 곧 그 숫자를 표시하는 2진수 바이너리 코드입니다. 즉, 0은 마지막 G핀만 제외하고 나머지가 모두 점등되어야 하므로 0b10111111입니다.

또한 여기서는 delay_secp10이란 함수를 썼는데, 이는 그 전에 delay_sec을 10등분 한 함수입니다. 1초에 dot point를 두번 깜빡여주고 싶어서 만들었습니다.

dp와 flag모두 두번깜빡여주기 위해 만든건데 사실 dp 깜빡임밖에 효과가 없으므로 빼고 진행하셔도 무방합니다.


3) 실행한다!


다음에 뵙겠습니다.

#AVR #ATmega 128 #ATmega128 #LED #ATmel Studio 7 #신호등 #아두이노 #전자전기


ATmega128로 신호등 만들기!


ATmega128이 무엇인지는 현대 인터넷 사회에서 다른 곳에서 엄청 자세히 나와있으므로 일단 실습으로 바로 넘어가겠습니다.


저도 처음입니다!


무작정 따라하기 입니다!


1) 배선한다

2) 프로그래밍 짠다

3) ATmega에 넣어준다!




빨간기판이 ATmega128입니다.

아래쪽 왼쪽에서 두번째가 GND(그라운드)이므로 -선을 연결하여 주시고, 바로 다음부터 있는 PA0번과 1번을 각각 빨간불, 초록불에 대응시켰습니다.


즉, 0번(혹은 1번)이 신호에 따라 +신호를 주고, 이것이 LED로 가서 불을 켜고, GND인 -로 들어오는 회로입니다.


사진에서는 LED가 너무 밝아 각 1K ohm의 저항을 달아주었습니다.


영상에서는 그냥 다이렉트로 꽂혀있는데, 영상쪽이 훨씬 직관적입니다.



아래는 소스코드 입니다.


#include <avr/io.h>


void delay_sec(int sec){

volatile int i, j, k;

for(k=0;k<sec;k++){

for (i=0;i<1000;i++){

for(j=0;j<1000;j++);

}

}

}


int main(void)

{

DDRA = 0b00000011;

while(1){

delay_sec(1);

PORTA = 0b00000000;

PORTA = 0b00000001;

delay_sec(1);

PORTA = 0b00000000;

PORTA = 0b00000010;

}

}


위에서 delay_sec함수는 ATmega128이 초당 16MHz의 처리 속도를 가지고 있는 것을 바탕으로 대략 근사한 값입니다.

정확히 1초는 아니고 1초보다 약간 더 오래 걸립니다.


정확한 1초는 나중에 또 포스팅하겠습니다.


그럼 다음에 또봐요!


+ Recent posts