Embedded System/AVR

[AVR] ADC 사용하기(13.11.25 코드 수정)

임베지수 2017. 4. 2. 14:40

AVR을 이용해 개발하는 사람들은 꼭 해보는 과정이 있다.

내가 배워온 과정을 순서대로 적으면 다음과 같다.(아마 제일 흔한 과정이 아닐까?)

 

 

IO핀을 이용하여 LED제어

타이머/카운터로 1초 만들기

PWM사용해서 서보모터 제어

캐릭터 LCD 동작구현

USART통신

ADC 사용해서 센서값 받아오기

 

 

여기까지 하면 나머지 부분은 스스로 데이터시트를 보면서 할 수 있다고 생각한다. PWM사용하는것 까진 쉽게 할 수 있어서 이렇게 따로 게시글로 정리하진 않았다. 그래도 캐릭터 LCD, USART까지 글을 올렸으니 ADC도 올리는게 맞는거겠지?

 

ADC는 뭐인고? Analog to Digital Converter의 약자다. 즉 아날로그 신호를 디지털값으로 바꿔주는 놈이다.

AVR의 ADC분해능은 10bit라서 받아오는 값을 1024등분 할 수 있다. 따라서 0~1023 범위의 값이 출력된다.

ADC는 거의 센서값을 받아올때 사용된다. 센서는 전압을 값으로 뿌려준다. 예를 들어 조도센서(CdS:황화카드뮴)는 빛의 세기에 따라 저항값이 변하는 놈이다. 빛이 밝으면 저항이 작아져서 전압이 높아지고 어두우면 전압이 낮아진다.

 

ADC를 처음 접하는 사람들이 자료를 검색하다 보면 생소한 단어에 당황하게 된다.

신호를 입력받는 방법은 두 가지가 있는데 싱글엔드가 어쩌고 저쩌고 차동입력이 어쩌고 저쩌고...난 그냥 생략한다.

일단 작동되게 하는게 중요하다고 생각하기 때문이다. 대신에 거리낌없이 남의 소스 긁어오는게 아니라 소스의 흐름을 이해하도록 노력해야한다.

 

일단 회로는 다음과 같다.

 

ADC0번 핀에 CdS 연결하고 AREF에 가변저항 연결해준다.

ADC를 사용하기 위해서는 레지스터 2개만 알면 된다. ADMUX랑 ADCSRA. 초간단해보이지만 아무것도 모르는 상태에서 해보면 어렵게 느껴질 수 있다. 사실 나도 엉뚱한곳에서 해매고 있었으니까.. 일단 데이터시트를 보자.

 

 

 

 

보자마자 AC...IC....하는 사람들이 있을거다. 초록색이 왜이렇게 많냐고,,, 하지만 내용은 별거아니다.

7,6번 비트는 기준전압을 뭘로 잡을거냐 하는거다. 아까 센서는 전압을 값으로 뿌린다고 했다. 0V를 0으로 할건지 1V를 0으로 할건지 5V를 0로 할건지 기준이있어야 한다. 그냥 00주면 된다. 대신 avr핀 중에 AREF라는 핀이 있는데 거기에 1K 가변저항 달고 5V 물려주면 된다. 왜냐하면 저항값에 따라 기준전압을 다르게 줄 수 있기 때문이다.

 

4~0번 비트는 ADC핀이 여러개있는데 어떤 핀을 사용할거냐, 입력방법은 어떻게 할거냐 설정해주는거다.

간편하게 ADC0번 핀에 조도센서 달거니까 00000 해준다.

 

다음으로,, 제일 중요한 ADCSRA!! 요놈을 건드려줘야 값을 받아온다.

 

 

AC...IC... 건드려야할게 왜이렇게 많아?!!! 라고 할 수 있겠지만 레지스터 2개니까 이해하자.

7번 비트는 ADC Enable이다. 즉 ADC 사용한다 이말이니까 꼭 1로 해줘야겠다.

6번 비트는 ADC Start Conversion이다. 즉 ADC변환 스타트!!.....탕~!! 하고 시작 총을 쏴주는 거니까 1로 설정.

5번 비트는 ADC Free Running이다. 스타트 했으니까 달려야지..한번 설정해주면 0 만들어주기 전까지 센서값 받아와줌.

2~0번 비트는 프리스케일러 지정해주는거다. 왜? ADC가 정상동작하기 위해서 50~200kHz의 클럭을 인가해줘야한다.

따라서 (내꺼는)16Mhz니까 2~0번에 111줘서 125kHz만들어 준다.

 

모든 설정이 끝났다. 이제 소스를 짜보자.

int get_value(void)
{
	ADMUX = 0x00;  //use AREF, adc channel 0
	ADCSRA = 0xe7; //adc enable, start, free running, prescaler 128

	while(ADCSRA&(1 << ADIF) == 0); // 변환완료 될 때까지 대기

	return ADC; // ADC값 반환
}

불러오기 쉽게 함수로 만들었다.

아래는 풀소스다. 저번 게시글을 참고하여 LCD와 UART통신을 이용하여 값을 볼 수 있도록 하였다.

 

#include <stdio.h>
#include <string.h>
#include "lcd.h"

unsigned char get_data(void)
{
	while(!(UCSR0A&0x80));
	return UDR0;
}

void send_data(unsigned char data)
{
	while(!(UCSR0A&0x20));
	UDR0 = data;
}

int get_value(void)
{
	ADMUX = 0x00;  //use AREF, adc channel 0
	ADCSRA = 0xc7; //adc enable, start, prescaler 128
	
	while((ADCSRA&(1 << ADIF)) == 0); // 변환완료 될 때까지 대기
	
	return ADC; // ADC값 반환
}

void main()
{
	int res = 0;
	unsigned char str[4];
	int i=0;
	char print_Arr[16];
	
	UCSR0A = 0x00; // ready flag clear
	UCSR0B = 0x18; // rx, tx enable
	UCSR0C = 0x06; // tx data len : 8bit
	
	UBRR0H = 0;
	UBRR0L = 51; // boudrate 19200
	
	DDRC = 0xff;
	DDRE = 0xff;
	DDRA = 0xff;
	
	PORTA = 0xaa;
	
	LCD_initialize();
	
	while(1)
	{
		res = get_value();
		
		str[3] = res/1000;
		str[2] = (res%1000)/100;
		str[1] = ((res%1000)%100)/10;
		str[0] = res%10;
		
		
		sprintf(print_Arr,"value:%d  ",res);
		LCD_string(0x80, print_Arr);
		
		while(i<4)
		{
			send_data(str[3-i]+48);
			i++;
		}
		i=0;
		send_data(10); // 줄바꿈(아스키코드로 10번)
		send_data(13); // 줄바꿈(아스키코드로 10번)
		_delay_ms(250);

		PORTA = ~PORTA;
		
	}
}

ADC값이 res에 저장되고 LCD에 뿌리기 위해 4자리 숫자로 분해하였다. LCD는 한번에 한글자만 전송가능하다!!!

 

작동영상

 

 

 

센서가 잘 안보긴 하지만 카메라가 다가가거나 손가락을 갖다대면 어두워서(저항이 커진다.) 센서값이 작아지고

 

밝아지면(저항이 작아진다.) 센서값이 커진다.