여기에서는 LED를 켜고 끄는 실험을 해 보도록 한다. LED는 파이썬 코드가 GPIO를 동작시키는 것을 시각적으로 확인할 수 있는 가장 간단한 도구이다. 다음 그림과 같이 LED를 저항과 직결한 뒤 12번 핀에 연결한다. 저항은 200Ω (혹은 그 이상) 이고 이것은 LED에 과전류가 흐르는 것을 막아주는 역할을 한다. LED는 극성이 있는 소자이다. 다리가 긴 쪽이 anode(+극), 짧은 쪽이 cathod(-극)이므로 연결할 때 방향을 주의해야 한다. 잘못 연결하면 LED가 켜지지 않는다. 짧은 다리는 저항과 직결하여 6번 핀(GND)에 연결하고 긴 쪽은 12번 핀과 연결하면 된다.


그림 9.2.1 LED 결선도 (긴 다리를 12번 핀에 연결)


 GPIO핀으로 LED를 제어하기 위해서 이 핀을 먼저 GPIO.setup()를 이용하여 출력으로 설정해야 한다. 그 이후에 GPIO.output()함수로 GPIO.HIGH 신호를 내보내면 LED가 켜지고 GPIO.LOW 신호를 내보내면 LED가 꺼진다.


blink.py

import RPi.GPIO as GPIO
import time

led = 12
GPIO.setmode(GPIO.BOARD)
GPIO.setup(led, GPIO.OUT)

for k in range(10):
   GPIO.output(led, GPIO.HIGH)
   time.sleep(0.5)
   GPIO.output(led, GPIO.LOW)
   time.sleep(0.5)

GPIO.cleanup()


이 프로그램은 LED를 10번 점멸시키고 난 뒤 종료된다. 이것을 실행하려면 다음과 같이 하면 된다.


sudo python3 blink.py


전에 언급한 바와 같이 반드시 앞에 sudo 명령을 붙여야 한다는 것이 유의하자.


 한 가지 언급할 것은 저항 값을 선택하는 방법이다. 데이터 쉬트에 의하면 LED에는 10mA 정도 흐르도록 하면 되고 이때 LED의 전압 강하가 1.7V 정도이다. 따라서 오옴의 법칙에 의해서 R = (3.3-1.7)/0.01 = 160Ω  을 얻을 수 있다. 이 결과로부터 160Ω 이상의 저항을 선택하되 너무 저항 값이 크면 불빛을 눈으로 확인하기 어려우므로 160~500Ω 범위의 저항을 선택하면 된다.

원문링크

Posted by 살레시오
,

 여기에서는 이전 포스트에 이어서 포트를 이용한 LED 실습을 진행한다.

LED실험 2

 두 번째 LED실험도 앞의 실험과 비슷하다. 이번에는 다음과 같은 순서로 1→2→3→4→1→2→3→4→1→… 계속 시차를 두고 반복하는 실험이다.


[표 1] 두 번째 LED 실험

순서

LED상태

이진수

16진수

1

●●●○○●●●

0b00011000

0x18

2

●●○●●○●●

0b00100100

0x24

3

●○●●●●○●

0b01000010

0x42

4

○●●●●●●○

0b10000001

0x81


프로그램 예는 다음과 같다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

int main(void) {

   uchar ucA, ucaRelay[4] = {0x18, 0x24, 0x42, 0x81};

   InitAM8();

   while(1) {

       for (ucA=0; ucA<4; ucA++) {

           LED(ucaRelay[ucA]);

           _delay_ms(200);

       }

   }

}


이 예를 보면 typedef 명령어로 unsigned char형은 byte형으로 재정의하였으며 앞으로도 이것은 계속 사용할 것이다. 그리고 LED로 순차적으로 내보낼 데이터는 배열로 처리하였음을 눈여겨 보기 바란다.


LED실험 3

 다음 [표 2]에 설명한 바와 같이 1→2→ … →13→14→1→2→ … 의 순서로 14가지 패턴을 반복하면 마치 LED한개가 좌우로 왕복하는 듯이 보이는 예제이다. 이 세 번째 LED실험은 앞의 것들과 달리 조금 복잡해 보인다.


[표 2] 세 번째 LED 실험

순서

LED상태

이진수

16진수

1

●●●●●●●○

0b00000001

0x01

2

●●●●●●○●

0b00000010

0x02

3

●●●●●○●●

0b00000100

0x04

4

●●●●○●●●

0b00001000

0x08

5

●●●○●●●●

0b00010000

0x10

6

●●○●●●●●

0b00100000

0x20

7

●○●●●●●●

0b01000000

0x40

8

○●●●●●●●

0b10000000

0x80

9

●○●●●●●●

0b01000000

0x40

10

●●○●●●●●

0b00100000

0x20

11

●●●○●●●●

0b00010000

0x10

12

●●●●○●●●

0b00001000

0x08

13

●●●●●○●●

0b00000100

0x04

14

●●●●●●○●

0b00000010

0x02


 이번 프로그램을 이전 실험과 같이 14개의 데이터를 포트에 순차적으로 내보내는 식으로 프로그램을 작성할 수도 있으나 그렇게 하면 너무 비효율적인 프로그램이 된다. 그래서 비트이동 연산자와 반복문을 이용하면 좀 더 간단하게 프로그램이 가능하다. 프로그램 예는 다음과 같다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

typedef enum DIR {UP, DOWN} EDIR;

int main(void) {

   uchar ucLed = 1;

   EDIR eDir = UP;

   InitAM8();

   while(1) {

       if (eDir == UP) {

           ucLed <<= 1;

           if (ucLed == 0x80)

               eDir = DOWN;

       } else { // if (eDir == DOWN)

           ucLed >>= 1;

           if (ucLed == 0x01)

           eDir = UP;

       }

       LED(ucLed);

       _delay_ms(200);

   }

}


LED실험 4 : LED탑 쌓기

 마지막으로 아래와 같은 패턴을 보여주는 프로그램을 작성해보자. 벽돌로 하나씩 탑을 쌓는 것처럼 보여서 ‘LED 탑 쌓기’실험이라고 이름을 붙여 보았다.


[표 3] 네 번째 LED 실험

순서

LED상태

이진수

16진수

1

○●●●●●●●

0b01111111

0x7F

2

●○●●●●●●

0b10111111

0xBF

3

●●○●●●●●

0b11011111

0xDF

4

●●●○●●●●

0b11101111

0xEF

5

●●●●○●●●

0b11110111

0xF7

6

●●●●●○●●

0b11111101

0xFB

7

●●●●●●○●

0b11111110

0xFD

8

●●●●●●●○

0b11111110

0xFE

9

○●●●●●●○

0b01111111

10

●○●●●●●○

0b10111111

11

●●○●●●●○

0b11011111

12

●●●○●●●○

0b11101111

13

●●●●○●●○

0b11110111

14

●●●●●○●○

0b11111101

15

●●●●●●○○

0b11111110

16

○●●●●●○○

0b01111111

17

●○●●●●○○

0b10111111

18

●●○●●●○○

0b11011111

19

●●●○●●○○

0b11101111

20

●●●●○●○○

0b11110111

21

●●●●●○○○

0b11111101

22

○●●●●○○○

0b11111110


이 실험은 이제 포트에 데이터를 순차적으로 내보내는 식으로는 프로그램이 거의 불가능하다. 이번에도 역시 비트이동연산자와 반복문을 조합하여 프로그램을 작성할 수 있다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

int main(void) {

  uchar ucaLEDTower[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };

   uchar ucled, uck, ucl;

   InitAM8();

   while (1) {

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

           ucled = 0x80;

           for (ucl=0;ucl<8-uck;ucl++) {

               LED( ucled | ucaLEDTower[uck] );

               _delay_ms(200);

               ucled >>= 1;

           }

       }

   }

}


위 프로그램은 길이는 짧지만 한 줄씩 읽어가면서 분석해 볼 여지가 있다. 다중 반복문을 사용하고 있는데 내부 반복문에서는 LED가 위에서 아래로 한 칸씩 떨어지는 것을 비트쉬프트 연산자로 처리하고 있으며 이 때 PORTB로 내보내는 데이터를 배열 byaLEDTower[]과 OR연산을 하여 마치 벽돌이 쌓여 있는 듯한 효과를 내는 것이다. 배열 byaLEDTower[]은 벽돌이 쌓인 모양의 데이터를 가지고 있다. 바깥 반복문에서는 떨어지는 루틴을 8번 반복하게끔 하는데 떨어지는 곳의 높이를 점차로 높인다.


 이번 포스트에서는 LED를 이용하여 간단한 예제들을 살펴보았다. 독자가 C언어의 문법을 잘 숙지하고 있다면 어떻게 동작이 되는지 쉽게 분석해 볼 수 있을 것이다.




Posted by 살레시오
,

 본 포스트에서는 ATmega8A의 포트에 LED어레이를 연결하여 LED를 구동시키는 실습을 진행한다. 실습을 위해서 LED 8개를 PB4:0와 PC4:0에 연결하며 회로도는 다음 그림과 같다. LED는 4비트씩 나누어서 포트B와 포트C에 연결하였다.


[그림 1] LED실습을 위한 회로도


이 회로도를 살펴보면 PD5핀이 스위칭 트랜지스터의 베이스와 연결되어 있음을 알 수 있다. 트랜지스터를 온시키기 위해서는 베이스에 아주 작은 양의 전류를 흘려주어야 하는데 보통 이를 위해서 큰 저항(수십~백kΩ)을 베이스와 포트핀 사이에 연결해야 한다. 하지만 본 실험에서는 외부 저항을 생략하고 그 대신에 ATmega8(A)내부에 내장된 풀업 저항을 이용하여 트랜지스터를 on시키는 방법을 사용하였다. 이를 위해서 DDRD5비트를 입력으로 (‘0’) 설정하며, 트랜지스터를 on시키기 위해서는 PORTD5 비트를 온(‘1’)시켜서 내부 풀업을 연결한다. 그 이후에 PB4:0와 PC4:0에 표시하려는 데이터를 내보내면 LED에 그 데이터가 표시되게 되는 것이다. (PD5핀과 PD6핀을 입력으로 설정해야 함에 주의해야 한다. 이 핀들을 출력으로 설정해 버리면 트랜지스터를 on/off시킬 수 없다.)


 또한, 포트핀의 내부 저항이 25Ω 정도인 것을 고려하면 외부 저항의 용량은 저항은 300Ω 정도가 적당하지만, 본 실습 장치는 USB전원으로 구동되므로 전류를 줄이고 LED가 켜졌음을 확인하는데 문제가 없는 수준에서 포트핀으로 흐르는 전류 값을 최소화하기 위해서 1KΩ의 저항을 연결하였다.

 만약 PB0부터 PB4까지가 모두 동시에 세트(‘1’된다 하더라도 포트B로 흘러나가는 전류의 합은 다음 식과 같이 계산할 수 있다.



여기서 1.7V는 LED양단의 전압 강하 값이다. 이것은 포트B로 흐르는 전류의 합이 100mA 이하여야 된다는 데이터쉬트상의 스펙을 벗어나지 않게 된다. 포트C에 대해서도 같은 계산 결과가 적용될 수 있을 것이다.


 다음과 같이 LED에 관련된 매크로와 함수를 정의한다.


#define sbi(sfr,bit) (_SFR_BYTE(sfr)|=_BV(bit))
#define cbi(sfr,bit) (_SFR_BYTE(sfr)&=~_BV(bit))
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;

#define TurnLEDOn ({sbi(PORTD,5);cbi(PORTD,6);})
#define Turn7SegOn ({sbi(PORTD,6);cbi(PORTD,5);})
#define TurnOffAll ({cbi(PORTD,5);cbi(PORTD,6);})
void LED(uchar b) { // Display b into LED
   TurnLEDOn;
   PORTB &= 0xF0;
   PORTC &= 0xF0;
   PORTB |= (b&0x0F);
   PORTC |= (b>>4);
}


 먼저 sbi(sft, bit)와 cbi(sft, bit) 매크로는 sfr 레지스터의 bit번을 세트시키는 매크로이다. 예를 들어 PORTB레지스터의 2번 비트를 세트시키고 싶다면 sbi(PORTB,2) 라고 하면 되고 PORTD 레지스터의 5번 비트를 리셋시키고 싶다면 cbi(PORTD,5) 라고 하면 된다. 또한 unsigned char형을 uchar 로 재정의 했는데 이는 긴 데이터 형의 이름을 짧게 사용하기 위한 것이다. uint 와 ulong 도 같은 이유로 재정의했다.

 LED와 관련된 매크로는 TurnLEDOn, Turn7SegOn, TurnOffAll 세 개가 정의되어 있는데 각각 LED만 켜고, 7세그먼트만 켜고, 둘 다 끄는 동작을 수행하는 매크로이다. 그리고 LED(uchar b)함수가 정의되어 있는데 이 함수에는 LED에 내보낼 데이터를 넘겨주면 된다. LED와 7세그먼트가 포트B의 하위 4비트와 포트C의 하위 4비트에 걸쳐 있기 때문에 LED( )함수의 내부에는 넘겨진 데이터의 하위 4비트를 PORTB4:0에, 그리고 상위 4비트를 PORTC4:0에 복사하는 코드가 작성되어 있다. 이 때 PORTB, PORTC레지스터의 상위 4비트는 그 값을 그대로 유지하도록 하였다.


LED실험 (1)

 첫 번째 실험은 [표 1]과 같이 4개씩 LED를 번갈아서 켜는 실험이다.


[표 1] 첫 번째 LED 실험

순서

LED상태

이진수

16진수

1

○○○○●●●●

0b00001111

0x0F

2

●●●●○○○○

0b11110000

0xF0


위의 표에서 1-2-1-2- 순으로 일정한 시차를 두고 무한히 반복하는 실험이다. 프로그램 예는 다음과 같다.


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

typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;

#define sbi(sfr,bit) (_SFR_BYTE(sfr)|=_BV(bit))
#define cbi(sfr,bit) (_SFR_BYTE(sfr)&=~_BV(bit))

#define TurnLEDOn ({sbi(PORTD,5);cbi(PORTD,6);})
#define Turn7SegOn ({sbi(PORTD,6);cbi(PORTD,5);})
#define TurnOffAll ({cbi(PORTD,5);cbi(PORTD,6);})
void LED(uchar b) { // Display b into LED
   TurnLEDOn;
   PORTB &= 0xF0;
   PORTC &= 0xF0;
   PORTB |= (b&0x0F);
   PORTC |= (b>>4);
}
int main(void) {
   InitAM8();
   uchar ucLED = 0x0F;
   while(1) {
       LED(ucLED);
       ucLED = ~ucLED;
       _delay_ms(500);
   }
}


이 프로그램을 보면 main()함수 안에서 while(1){ } 반복문에 의해서 무한루프에 빠지게 프로그램이 되어 있다. 일반적으로 PC상에서는 운영체제 (Operation System, OS로 줄여 표기하고 윈도우, 리눅스 등이 있다.)가 응용프로그램을 실행시키고 응용프로그램이 종료가 되면 다시 OS로 되돌아가지만 uC는 OS가 없기 때문에 응용프로그램 안에서 프로그램 실행이 계속 머물러야 하므로 무한 루프가 이용된다.


 전술한 바와 같이 LED를 켜는 코드와 7세그먼트를 켜는 코드를 매크로 TurnLEDOn, Turn7SegOn 으로 각각 정의해 놓고 있어서 LED를 구동시키고 싶을 때는 TurnLEDOn이라는 매크로를 사용하면 된다. 이렇게 매크로를 정의해 놓은 이유는 사용의 편의성 때문이기도 하지만 프로그램의 가독성 때문에도 그렇다. 예를 들어서 main()함수 중간에


sbi(PORTD,5);
cbi(PORTD,6);


라고 작성되어 있다고 가정해보자. 다른 사람이 이 프로그램을 읽는다면 한 눈에 이 두 줄이 무슨 역할을 하는지는 알기 힘들 것이다. 하지만


TurnLEDOn;


이라고 작성하면 이것은 이전의 것과 똑같은 코드를 생성하지만 (전처리기에 의해서 이것은 ({sbi(PORTD,5);cbi(PORTD,6);})로 컴파일하기 전에 치환된다.) 한 눈에 LED를 켜는 코드라고 알아볼 수 있을 것이다.



Posted by 살레시오
,

 고휘도 LED는 이전 실험에서 사용된 일반 LED보다 밝기가 대폭 개선된 LED이다.


[그림 1] 고휘도 LED

일반적인 LED와 외형은 큰 차이가 없지만 밝기가 획기적으로 개선된 것이고 전력 효율이 높아서 가정용 조명이나 신호등, 차량의 전조등 등으로 널리 사용된다.


 구동 회로는 일반 LED와 다르지 않게 저항을 직결하여 전원이 연결하면 되는데 문제는 저항값으로 어떤 값을 사용하는가이다. 데이터쉬트를 살펴보면 최대 허용 전류는 20mA 이고 (전류가 클수록 더 밝다.) 이 전류가 흐를 때 다이오드 양단의 전압 강하는 3.0~3.6V 이다. 따라서 만약 5V 전원을 사용하고 최대 전류를 흘릴 때 전압강하가 3.0V라고 가정하면 저항값은 오옴의 법칙에 의해서 다음과 같이 간단히 계산할 수 있다.



이 저항값이 허용되는 가장 최소 저항이므로 이것보다 큰 용량의 저항을 선택하면 구동하는데 무리가 없을 것이다. 실습에는 100 Ω의 저항을 선택했다.


첫 번째 실험

 첫 번째 예제로 서서히 밝아졌다가 다시 서서히 어두워지는 동작을 하는 프로그램을 작성해 보자.


#define HLED 5
void setup() {
   pinMode(HLED,OUTPUT);
}
void loop() {
   for (int k=0; k<256; k++) {
       analogWrite(HLED,k);
       delay(15);
   }
   for (int k=255; k>=0; k--) {
       analogWrite(HLED, k);
       delay(15);
   }
}


위 프로그램에서 delay(15) 함수를 이용하여 서서히 밝아지거나 서서히 어두워지는 효과를 내었다.


두 번째 실험

 두 번째로 스위치를 누르면 고휘도 LED가 서서히 켜지는 프로그램을 작성해 보자. 완전히 꺼진 상태에서 최고 밝기로 켜지는 시간은 4초로 설정한다. 버튼을 떼면 그 즉시로 고휘도 LED가 꺼져야 한다.

#define HLED 6
#define SW 3
void setup() {
   pinMode(HLED,OUTPUT);
   pinMode(SW,INPUT_PULLUP);
}
int iL=0;
void loop() {
   if (digitalRead(SW)==LOW) {
       analogWrite(HLED, iL++);
       if (iL>255) iL = 255;//❶
       delay(15);
   } else {
       digitalWrite(HLED,LOW);
       iL = 0;
   }
}

전술한 바와 같이 analogWrite()함수의 두 번째 인수의 범위는 0~255이다. 따라서 ❶에서 이 범위를 넘으면 (즉 256이 되면) 255값을 계속 가지도록 if 문으로 처리했음을 유의해서 보자.


세 번째 실험

 이전 실험에서는 스위치를 떼면 그 즉시 꺼졌지만 이번 예제에서는 서서히 꺼지는 부분을 추가해 보자. 꺼지는 속도는 완전히 켜졌을 때에서 완전히 꺼질 때까지의 시간이 4초 정도 되게 설정한다.


#define HLED 6
#define SW 3
void setup() {
   pinMode(HLED,OUTPUT);
   pinMode(SW,INPUT_PULLUP);
}
int iL=0;
void loop() {
   if (digitalRead(SW)==LOW) {
       analogWrite(HLED, iL++);
       if (iL>255) iL = 255; //❶
       delay(15);
   } else {
       analogWrite(HLED, iL--); //❷
       if (iL<0) iL = 0;
       delay(15);
   }
}


이 프로그램의 동작은 스위치를 누르고 있으면 서서히 켜지고 떼면 다시 서서히 꺼지게 된다. analogWrite()함수의 두 번째 인수의 범위는 0~255이다. 따라서 ❶과 ❷에서 이 범위를 넘으면 그 한계값을 계속 가지도록 if 문으로 처리했음을 유의해서 보자.



Posted by 살레시오
,

 아두이노 우노에는 실습용 LED가 실장되어 있는데 이것은 13번 핀과 연결되어 있다. 아래 그림과 같이 아두이노마크 근처에 위치하고 있다.(빨간 원 안)


[그림 1] 아두이노 우노의 내장 LED


이것을 이용하여 깜박이는 예제는 아두이노 IDE에 미리 탑재되어서 제공되는데 다음과 같이 예제파일을 선택하여 읽어들일 수 있다.


[그림 2] Blink 예제 읽어들이기


소스코드는 다음과 같다. (주석문은 삭제하였다.)


int led = 13;
void setup() {
   pinMode(led, OUTPUT);
}
void loop() {
   // turn the LED on (HIGH is the voltage level)
   digitalWrite(led, HIGH);
   delay(1000); // wait for a second

   // turn the LED off by making the voltage LOW

   digitalWrite(led, LOW);
   delay(1000); // wait for a second
}


이 예제의 구조를 살펴보면 먼저 눈에 띄는 것이 있는데 setup() 함수와 loop() 함수이다. 이 두 함수는 아두이노 프로그램의 전체적인 구조를 잡아주는 역할을 한다.


    ⁎ setup() 함수

       • 아두이노 프로그램이 실행될 때 맨 처음에 단 한 번 호출되어 수행된다.

       • 따라서 여기에 각종 장치를 초기화하거나 초기값을 설정하는 코드가 오게 된다.


    ⁎ loop() 함수

        • setup()함수 실행 후 수행되면 계속 반복 수행된다.

        • 아두이노에 연결된 장치들을 구동시키는 코드가 위치한다.


setup()함수 내에서 pinMode()함수를 사용하는데 이 함수는 핀을 입력(INPUT) 혹은 출력(OUTPUT)으로 사용할 지를 설정하는 함수이다. 첫 번째 인자로 핀의 번호가 두 번째 인자로 INPUT, 혹은 OUTPUT이라는 상수를 넘겨주면 된다. pinMode(led, OUTPUT) 은 led(13번)을 출력(OUTPUT)으로 사용하겠다고 설정하는 것이다.


   ⁎ pinMode(pinNumber, INPUT/OUTPUT) 함수

       • pinNumber 핀을 입력(INPUT) 혹은 출력(OUTPUT)으로 사용할 지를 지정한다.

       • pinNumber는 우노의 경우 0,1,2, … 13, A0, A1, ...A6 중 하나이다.


loop()함수에서는 digitalWrite()함수를 사용했는데 이 함수는 핀으로 출력값을 내보내는 작업을 수행한다. 첫 번째 인자로 핀 번호를 받고 두 번째 인자로 HIGH 혹은 LOW 상수를 받는다. digitalWrite(led, HIGH) 명령은 led 핀에 HIGH 신호를 내보내므로 LED가 켜지게 된다. 반대로 digitalWrite(led, LOW) 명령은 led 핀에 LOW 신호를 내보내므로 LED가 꺼지게 된다.


    ⁎ digitalWrite(pinNumber, HIGH/LOW) 함수

         • pinNumber 핀이 출력일 경우에 사용한다.

         • 1값을 내보낼지 (HIGH) 0값을 내보낼지(LOW)를 지정한다.


delay()함수는 입력된 시간만큼 아무 일도 안하고 멈춰있는 동작을 수행한다. delay(1000)은 1000ms 동안 지연시키는 것이다. 입력받는 숫자는 ms 단위이다.


    ⁎ delay(time) 함수

         • time ms 만큼 지연 (아무런 일도 안 하고 멈춰있음)한다.


이 함수의 입력 인자가 밀리세컨드(ms) 단위임을 유의해야 한다.


아두이노 강좌 전체 목록 (TOP) >>>

C++ 언어 전체 강좌 목록 >>>

c{ard},n{ad007}

Posted by 살레시오
,