2.7 아날로그 입력             c{mpy0207}


ESP8266은 아날로그 입력핀 하나를 가지고 있다. 이 핀을 이용하면 0V~1.0V사이의 전압을 읽어서 숫자 0~1023 사이의 값으로 변환할 수 있다.


MicroPython에서는 다음과 같이 machine모듈의 ADC클래스를 이용하여 객체를 생성하면 된다.


from machine import ADC
a0 = ADC(0)
print(a0.read())

read()메서드의 값은 0과 1023사이의 정수이며 1023일때 핀의 전압이 1.0V라는 의미이다.

A0핀에 1.0V 이상의 전압이 가해지면 안되므로 적절한 전압분배회로를 이용해야 한다.


Posted by 살레시오

댓글을 달아 주세요

 실험에서는 AVCC 를 단순히 5V에 연결하고 AREF는 연결을 하지 않았으며 [그림 1] 에 A/D변환기의 전원부를 도시하였다. 데이터쉬트에 의하면 AVCC는 LC필터를 이용하여 안정화시킬 것을 권장하고 있다.(여기에서는 사용하지 않음) 또한 앞으로의 실습에서는 기준전압을 AVCC로 사용할 것이기 때문에 AREF핀은 연결하지 않았다. 데이터쉬트에서는 AREF핀을 사용하지 않을 경우 GND와 0.1uF의 커패시터를 연결할 것을 권하고 있으나 이 역시 A/D변환 회로부에 들어가게 될지도 모르는 전원 잡음을 조금이라도 줄이기 위한 방편이며, 연결하지 않아도 실습하는데 있어서 크게 문제될 것은 없다.

[그림 1] 그림 8.3.1 데이터쉬트의 권장 회로(좌)와 실험키트의 회로(우)


Cds광센서를 이용한 ADC 실험

 여기에서는 Cds광센서를 이용하여 A/D변환 실험을 수행해 보겠다. 설명의 편의를 위해 회로도를 [그림2]에 도시하였다.


[그림 2] CdS 셀 회로도


Cds는 빛의 양에 따라 저항값이 반비례하는 가변 저항으로 보면 된다. 따라서 내부 풀업 저항을 연결한다면 광량에 따라서 PC4핀의 전압값이 달라질 것이다. 극단적인 경우로 Cds의 저항이 무한대라면(빛이 하나도 없는 경우) PC4핀의 전압은 5V일 것이다. 반대로 저항이 0이라면 (빛이 매우 많은 경우이다) PC4핀의 전압은 0V일 것이고 그 중간의 경우는 광량에 따라서 PC4핀의 전압값이 반비례할 것이다. 즉, 광량이 많으면 PC4핀의 전압은 낮아지고, 광량이 적어질수록 전압은 높아진다.


 이러한 특성을 이용하여 밝으면 LED가 꺼지고 어두워질수록 LED가 더 많이 켜지는 프로그램을 작성해 보자.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "Am8USBasp.h"
int main(void) {
   uint uiA;
   uchar ucB;
   InitAM8();
   ADMUX = 0b01000100; // AVCC, right-align, ADC4
   ADCSRA = 0b10100111; // free running, no interrupt, Div128
   sbi(ADCSRA, ADSC); // start conversion
   while(1) {
       uiA = ADC;// read ADC register value
       if (uiA < 256)
           ucB = 0b00000000;
       else if (uiA < 384)
           ucB = 0b00000011;
       else if (uiA < 512)
           ucB = 0b00001111;
       else
           ucB = 0b11111111;
       LED(ucB);
   }
}


이 예제에서는 기준전압을 AVCC로, 변환 데이터 저장 방식은 우측 정렬로 그리고 핀은 ADC4번 핀으로 선택하여 ADMUX레지스터에 설정하였다. 그리고 인터럽트는 사용하지 않고 연속 변환 모드와 128분주된 클럭으로 선택하여 ADCSRA레지스터에 설정하였다. 그리고 ADCSRA레지스터의 ADSC비트를 세트시켜서 변환을 시작시켰으며 그 이후에는 자동으로 A/D변환을 수행하여 ADC레지스터의 값이 실시간으로 갱신이 된다. 따라서 while(1) 반복문 안에는 단순히 ADC레지스터 값을 읽어서 그것의 범위에 따라서 LED의 숫자를 조절하는 프로그램이 작성되어 있다. A/D변환값이 10비트이기 때문에 그 값의 범위는 0~1023일 것이다.

 

 이 프로그램을 컴파일 한 후에 실행시켜서 만약 밝은 환경이라면 LED가 하나도 안 켜지지만 손바닥으로 센서를 많이 가릴수록 LED가 더 많이 켜지는 것을 확인할 수 있을 것이다.



Posted by 살레시오

댓글을 달아 주세요

 여기에서는 이전 포스트들 (ADC 개요, 관련 레지스터들) 에 이어서 A/D변환을 실제로 수행하는 절차에 대해서 정리하였다.

① 기준전압, 입력핀, 그리고 저장방식을 결정한 후 ADMUX 레지스터를 적절히 설정한다.

② 클럭분주비, 인터럽트 허용 여부 등을 결정한 후 ADCSRA 레지스터를 설정하여 A/D 변환기의 동작을 허용한다.

③ ADSC비트를 ‘1’로 써서 A/D변환을 시작한다.

④(A) 만약 변환 완료 인터럽트를 사용하지 않는다면 ADIF비트가 ‘1’이 될 때 까지 기다린 후 ADC레지스터 값을 읽는다. 그후 ADIF비트를 다시 클리어(‘0’)시킨다.

④(B) 만약 변환 완료 인터럽트를 사용한다면 ISR()함수에서 ADC레지스터 값을 읽는다.

 위에서 ④(A)번을 보면 A/D변환을 시작한 이후 완료되었는지를 검사하는 비트는 보통 ADIF를 사용함에 유의해야 한다. ADIF비트가 ‘1’로 세트되었다는 것은 A/D변환이 완료되고 그 변환값이 ADC레지스터에 저장되었음을 의미한다. 따라서 ADIF가 ‘1’이 될 때 까지 기다렸다가 그 이후에 변환값을 저장한다.


 만약 인터럽트를 사용하지 않는다면 ADIF 비트를 수동으로 클리어(‘0’)시켜야 하는데 데이터쉬트에 의하면 이를 위해서는 ADIF비트에 ‘1’을 써야한다는 것에 유의해야 한다. 만약 인터럽트를 사용하여 ISR(ADC_vect)가 호출된다면 자동으로 ADIF는 ‘0’으로 클리어된다.




Posted by 살레시오

댓글을 달아 주세요

 여기에서는 이전 포스트에 이어서 ACD에 관련된 레지스터들에 대해서 알아보도록 하겠다.

[ADCH:ADCL] 레지스터

 A/D 변환 결과는 10비트이기 때문에 SFR 두 개가 필요한데 ADCH과 ADCL 두개의 레지스터가 변환된 값이 저장되는 레지스터이다. 변환값의 상위 바이트와 하위 바이트를 저장하는데 각각 사용되고 ADC의 최대 분해능이 10비트이기 때문에 이중에 10개의 비트만이 변환 값을 저장하는데 사용된다. 뒤에 설명할 ADMUX 레지스터의 ADLAR 비트의 값에 따라 저장하는 방식은 두 가지가 있는데 이를 [그림 1]과 [그림 2]에 도시하였다.


[그림 1] ADLAR='0' 일 때 변환 결과를 저장하는 방식 (우측 정렬)


[그림 2] ADLAR='1' 일 때 변환 결과를 저장하는 방식 (좌측 정렬)


변환 값을 10비트 모두 사용하고자 한다면 [그림 1]과 같이 ADLAR='0'으로 설정한 후 상위 6개의 비트는 무시하고 사용하면 된다. 하지만, 변환 결과를 8비트만 쓰고 싶을 때에는 [그림 2]와 같이 ADLAR='1'로 설정한 후 ADCH 값만을 취하여 사용하면 된다. 이후로는 16비트 메모리 [ADCH:ADCL]을 줄여서 ADC라고 표기한다. A/D 변환 결과는 다음과 같은 식으로 계산할 수 있다.

여기서 Vin 은 선택된 입력핀의 전압값이고 Vref 는 기준전압이다.

ADCSRA 레지스터

 ADCSRA (ADC Control and Status Register) 레지스터는 ADC의 전반적인 동작을 제어하는 기능을 수행한다. 구조는 다음과 같다.


[그림 3] ADCSRA 레지스터의 구조


• ADEN (ADc ENable) 비트는 ‘1’로 설정해야 ADC의 모든 동작이 허용된다. ‘0’으로 설정하면 ADC의 모든 동작이 금지되며 만약 변환 중에 ‘0’으로 클리어되면 변환이 즉시 중지된다.

• ADSC (ADc Start Conversion) 비트가 ‘1’로 세트되면 ADC가 시작된다. 연속 변환 모드에서는 이 비트가 세트되면 첫 번째 변환이 수행된 이후 자동으로 계속 변환이 수행된다. ADEN 비트가 ‘1’로 설정된 이후의 최초의 변환은 ADC의 초기화 때문에 25 클럭이 소요되고 두 번째 변환부터는 13클럭이 소요된다. 변환이 진행 중이라면 이 비트를 읽으면 ‘1’ 값이 읽혀지고 변환이 완료되면 ‘0’값으로 되돌아간다.

• ADFR (ADc Free Running select) 비트는 ADC를 연속 변환 모드로 설정한다. 연속 변환 모드에서는 ADC 변환이 자동으로 연속적으로 일어나므로 임의의 시점에서 최신 변환 결과를 읽어들일 수 있다.

• ADIF (ADc Interrupt Flag) 비트는 A/D 변환이 완료되어 ADC 레지스터 값이 갱신되고 나면 ‘1’로 세트되며, 만약 ADIE 비트가 ‘1’로 세트된 상태라면 인터럽트를 요청하게 된다. 해당 ISR이 호출되면 ADIF 비트는 자동으로 ‘0’으로 리셋되며, 만약 사용자가 강제로 이 비트를 ‘0’으로 클리어하려면 이 비트에 ‘1’을 써주어야 한다.

• ADIE (ADc Interrupt Enable) 비트를 ‘1’로 설정하면 A/D 변환완료 인터럽트를 허용한다. 실제로 인터럽트가 발생하려면 SREG 레지스터의 I비트가 ‘1’로 설정되어 있어야 한다.

• ADPS2:0 비트들은 ADC에 인가되는 클럭의 분주비를 선택한다. A/D변환기에서 사용하는 클럭은 10비트 연속 변환 모드가 정상적으로 수행되기 위해서 50~200KHz 범위 이내이어야 한다고 데이터쉬트에 기록되어 있다. 따라서 본 실습키트의 경우처펌 시스템클럭이 16MHz일 때에는 정상적인 A/D변환을 위해서는 <표 8.3.1>에 표시한 바와 같이 ADSP2:0='110' 혹은 ADSP2:0='111' 이어야 한다.

단, 10비트보다 낮은 분해능으로 사용할 경우에는 200KHz보다 더 높은 주파수를 사용할 수 있다고 되어 있다.


[표 1] ADC로 인간되는 클럭의 분주비

ADSP2

ADSP1

ADSP0

분주비

clksys=16MHz 일 경우

0

0

0

2

8,000KHz

0

0

1

2

8,000KHz

0

1

0

4

4,000KHz

0

1

1

8

1,000KHz

1

0

0

16

500KHz

1

0

1

32

250KHz

1

1

0

64

125KHz

1

1

1

128

62.5KHz


ADMUX 레지스터

 ADMUX (ADc MUltipleXer selection register) 레지스터는 ADC의 입력 핀을 선택하는 기능과 기준 전압원을 선택하거나 변환 결과를 레지스터에 저장하는 형식을 지정하는 기능을 수행한다.


[그림 4] ADMUX레지스터의 구조


• REFS1:0 두 비트는 A/D 변환의 기준 전압을 선택한다.


[표 2] ADC 기준 전압 선택

REFS1

REFS0

기준전압 선택

0

0

AREF에 인가되는 전압

0

1

AVCC에 인가되는 전압 (1)

1

0

-

1

1

내부 2.56V 전압 (1)

(1) AREF핀에 외부 전압을 인가하지 않고 GND와 0.1uF의 커패시터를 연결할 것.


• ADLAR (ADc Left Adjust Result) 비트는 ‘1’로 설정되면 A/D 변환 결과가 ADC레지스터에 저장될 때 <그림 8.2.2>와 같이 정렬되고 ‘0’으로 설정되면 <그림 8.2.1.>과 같이 정렬된다.

• MUX3:0 비트들은 ADC의 입력핀을 선택한다. PDIP패키지는 외부 핀이 ADC5번까지만 있고, TQFP/MLF 패키지는 ADC7번까지 있다.


[표 3] 입력 채널 선택

MUX3:0

단극성입력

(single ended input)

비고

0000

ADC0

PDIP 패키지

0001

ADC1

0010

ADC2

0011

ADC3

0100

ADC4

0101

ADC5

0110

ADC6

TQFP/MLF 패키지에만 있음

0111

ADC7

1000

~1101

-

1110

1.30V (VBG)

1111

0V (GND)



Posted by 살레시오

댓글을 달아 주세요

 A/D변환기 (Analog-to-Digital Converter)란 아날로그 전압을 디지털 값으로 변화시켜주는 장치로서 ATmega8(A)는 10비트 분해능의 A/D변환기를 내장하고 있다. 개략적으로 설명하면 변환하는 전압의 범위가 0~5V일 때 입력핀의 전압이 0V이면 0x000, 5V이면 0x3FF (0b11.1111.1111), 2.5V이면 0x1FF와 같이 연속적인 전압을 불연속적인 2진 숫자로 바꾸어주는 장치가 A/D변환기이다.


[그림 1] ATmega8(A) 의 ADC 내부 구조도


A/D변환기는 보다 안정된 동작을 위하여 AVR의 시스템 전원과 별도로 아날로그 회로의 전원핀 AVCC가 있으며 A/D변환에 필요한 기준 전압을 인가하는 AREF핀도 가지고 있다. 단, AVCC의 전압은 VCC에 인가되는 전압의 ±0.3V 이내로 유지되어야 한다. 이를 위해서 dateasheet에는 다음과 같은  간단한 LC 필터를 AVCC에 연결하도록 권장된다.


[그림 2] AVCC에 연결되는 LC필터


변환되는 전압의 최소값은 GND이며, 최대값은 AVCC/AREF/2.56V(내부 발생) 세 가지 중에서 선택적으로 설정할 수 있다.


[그림 2] ADC와 관련된 핀들


A/D 변환 핀은 PDIP패키지의 경우 ADC0~ADC5로 6개가 있으며 SFR의 설정에 따라 이들 중 하나의 핀을 선택하여 A/D 변환을 수행하게 된다. 단일 변환 시간은 13~250us이다.


 A/D변환 모드는 단일 변환 모드 (single conversion mode)와 연속 변환 모드 (free running mode)가 있는데 연속 변환 모드에서는 최대 분해능으로 초당 15,000번의 속도로 연속 변환을 수행토록 할 수 있다. A/D변환이 완료되면 변환 결과 값이 저장되는 SFR이 갱신되면서 A/D 변환 완료 인터럽트가 발생하도록 설정할 수도 있다.



Posted by 살레시오

댓글을 달아 주세요

 여기에서는 온도 센서 LM35DZ를 이용하여 아날로그 입력 실험을 수행하도록 하겠다. 이 소자의 외형과 핀의 기능은 다음 그림과 같다. 세 개의 다리가 있는데 각각 5V, 출력, GND 이고 가운데 출력핀을 아두이노의 아날로그 핀에 연결해야 한다.


[그림 1] 온도센서 LM35-DZ


데이터시트에 따르면 이 소자는 섭씨 +2도~+150도 의 범위를 측정할 수 있으며 섭씨 1도는 약 10mV 의 전위차를 갖는다. 예를 들어서 센서핀의 전압이 0.21V라면 210mV 이므로 온도는 21도씨라는 것이다. 따라서 아날로그 값을 읽어들여서 약간의 계산(전압값에 100을 곱한다)을 거쳐서 섭씨 온도가 얻어진다.


 만약 A0 핀에 센서가 연결되어 있다고 하면 이를 수행하는 코드는 다음과 같다.


short sVal = analogRead(A0); // 아날로그값: 0~1023의 값을 갖는다.
float fVoltage = sVal*5.0/1024; // 실제 전압값을 변환 (5.0을 곱함에 유의한다.)
float fTemp = fVoltage * 100; // 전압값을 섭씨 온도로 변환한다.


이제 이것을 함수로 구현하여 1초마다 한 번씩 온도를 감지하여 PC로 시리얼 통신을 통해서 표시해주는 예제는 다음과 같다.


void setup() {
   Serial.begin(9600);
}

void loop() {
   Serial.println(getTemp());
   delay(1000);
}

float getTemp() {
   short sVal = analogRead(A0);
   float voltage = sVal*5.0/1024; // 실제 전압값을 구한다.
   return voltage*100;
}

[그림 5.2.2] 1초마다 온도를 감지하여 표시하는 결과 화면


 만약 기준 전압으로 내부 전압인 1.1V를 사용하면 분해능이 약 5배 정도 높아지므로 OP-AMP와 같은 외부 회로 없이 간접적으로 5배 증폭의 효과를 볼 수 있다. 1.1V라도 섭씨 110도에 해당되므로 실온을 측정하는 경우라면 크게 문제가 없을 것이다. 다음과 같이 앞의 예제를 조금만 수정하면 된다.


#define SUPPLY_VOLTAGE 1.1

void setup() {
   Serial.begin(9600);
   analogReference(INTERNAL);
}

void loop() {
   Serial.println(getTemp());
   delay(1000);
}

float getTemp() {
   short sVal = analogRead(A0);

   // 1.1을 곱한 뒤 1024로 나눈다.

   float voltage = sVal*SUPPLY_VOLTAGE/1024;
   return voltage*100;
}


 한 가지 더 부연할 것은 실제 온도와 센싱된 온도와는 아무래도 차이가 나게 마련이다. 따라서 정밀하게 온도를 잴 수 있는 온도계 값과 센싱된 값을 비교하여서 그 차이만큼 보정을 해주면 좀 더 정확하게 온도를 잴 수 있을 것이다.


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


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


c{ard},n{ad022}



Posted by 살레시오

댓글을 달아 주세요

 아두이노 우노의 A0부터 A5 까지 6개의 핀을 이용하여 아날로그 입력을 받을 수 있는데 디지털 핀이 0과 1 두 값 만을 입출력으로 가지는 것과는 달리 아날로그 핀은 그 전압을 0~1023 의 정수값으로 변환하여 읽어들인다.


[그림 1] A0~A5 핀과 AREF핀

A0~A5핀으로 아날로그 입력을 받는다.

AREF핀은 아날로그 기준 전압을 설정한다.


아날로그 핀은 0~5V 사이의 전압 값을 0~1023 사이의 정수값으로 변환시킨다. 이것을 A/D변환(analog-to-digital conversion)이라고 한다. 따라서 분해능은 0.0049V (4.9mV = 5V/1024)이며 이 때 사용되는 함수가 analogRead()함수이다.


ananlogRead(pin)


입력 인수로 0(또는 A0), 1(또는 A1), … 5(또는 A5)를 주고 리턴값은 int형으로서 앞에서 언급한 대로 0~1023 값이다. 변환 시간은 100micro-sec 으로서 이론상으로는 1초에 10000번 정도 변환이 가능하다. 아날로그 핀은 디지털 핀과 달리 기본적으로 입력으로 설정되어 있으므로 별도로 입력을 설정하는 과정 없이 바로 위의 함수를 이용할 수 있다.


 한 가지 알아두어야 할 점은 아두이노 우노의 경우 다음 세 가지 방법은 같은 동작을 수행한다.


[표 1] 아두이노 우노의 analogRead()함수 동작에 사용되는 상수

방법1

방법2

방법3

비고

analogRead(0)

analogRead(A0)

analogRead(14)

A0 == 14

analogRead(1)

analogRead(A1)

analogRead(15)

A1 == 15

analogRead(2)

analogRead(A2)

analogRead(16)

A2 == 16

analogRead(3)

analogRead(A3)

analogRead(17)

A3 == 17

analogRead(4)

analogRead(A4)

analogRead(18)

A4 == 18

analogRead(5)

analogRead(A5)

analogRead(19)

A5 == 19


즉, analogRead(0)과 analogRead(A0) 그리고 analogRead(14) 는 내부적으로 같은 동작을 수행한다. 왜냐면 상수 A0, A1, …, A5 는 내부적으로 다음과 같이 14,15,...,19로 정의되어 있기 때문이다.


              #define A0  14

              #define A1  15

              ….

              #define A5  19


(필자는 처음에 상수 A0 는 내부적으로 0으로 정의되었을 것으로 짐작을 했었는데 그게 아니었다.) 보통은 [표 1]의 <방법1> 혹은 <방법2> 를 사용하고 <방법3>은 사용하지 않지만 내부적으로 A0상수가 14값을 갖는 다는 것은 알아두는 것이 좋은데 그 이유는 아날로그 핀을 디지털 핀으로 사용할 때 이 상수가 사용되기 때문이다. 디지털 핀이 13번 까지 있으므로 그 다음 숫자부터 아날로그 핀에 할당되었다는 것을 알 수 있다.


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

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

c{ard},n{ad019}

Posted by 살레시오

댓글을 달아 주세요