atmega 128로 dc 모터제어를 꾸미고 있습니다.
그런데 제가 가변저항으로 %를 바꾸는 형식으로 모터 제어를 할려고 하는데
프로그래밍을 할때 가변저항을 따로 프로그래밍을 해야 한다고 하는데 어떻게 하는건지 모르겠네요..
adc1을 통해서 가변저항을 연결했습니다..
능력자 계시면 프로그래밍 소스 힌트나 좀 올려주시면 감사하겠습니다.
열심히 활동할께요..ㅠㅠ
그런데 제가 가변저항으로 %를 바꾸는 형식으로 모터 제어를 할려고 하는데
프로그래밍을 할때 가변저항을 따로 프로그래밍을 해야 한다고 하는데 어떻게 하는건지 모르겠네요..
adc1을 통해서 가변저항을 연결했습니다..
능력자 계시면 프로그래밍 소스 힌트나 좀 올려주시면 감사하겠습니다.
열심히 활동할께요..ㅠㅠ
AVR 마이크로프로세서의 A/D converter활용 실습
1. 실험 실습 내용 및 학습 목표
l AVR마이컴의 내장 A/D컨버터 활용법 이해
l LM 35 온도 센서의 활용법 실습
l LM385 voltage reference 의 이해와 응용법
l A/D 컨버터 관련 레지스터 사용법 이해
2. 실험 실습 준비 항목
l 실습회로 보드 (KEST-B1 보드)
l Atmega8535 CPU, 5V전원, 4MHz 혹은 8MHz Crystal 1개, 22pF 칩 콘덴서 2개
l Reset버튼용 스위치 1개, 10K옴 저항 1개
l 16x2 캐릭터 LCD 1개 , 가변 저항 10K옴 1개
l LM35 온도센서 1개, LM385-1.2 전압 레퍼런스 칩 1개, 포텐셜미터 5K 1개
l WinAVR GCC 컴파일러
l Text Editor ( 각자 편리한 에티터를 사용하면됨 )
l PonyProg 프로그램 v.2.6 이상 ( v.2.6 이상에서만 ATmega8535를 지원)
l ISP 모듈과 Serial Cable 및 RS232C통신 포트를 갖춘 Window기반 PC 1대
3. 핵심 학습 내용
이번 실험에서는 가장 유용하게 생활에 활용할 수 있는 온도센서의 AVR interface 방법에 대해 학습하고,실험해 볼것이다. LM35온도 센서와 LM385 전압레퍼런스 칩이 필요하며, 레퍼런스 전압을 조절하기 위하여, 정밀 가변저항인 포텐셜미터가 필요하다. 만일 온도를 0.5’C단위로 대략적으로 측정 한다면, LM385 전압레퍼런스는 사용하지 않아도 되겠지만, 이럴경우 시스템에 입력되는 전원 전압의 변동에 따라서 같은 온도 상태에서도 측정 온도 값이 변동될수 있으므로, 우리는 여기서 LM385 전압 레퍼런스를 함께 사용하여 이러한 온도 오차를 조금이나마 줄여 보도록 하겠다. LM385는 입력 전원의 변동에 상관없이 언제나 1.2V혹은 LM385-2.5의 경우엔 2.5V의 기준 전압을 출력해 준다. 우리가 사용하는 칩은 LM385-1.2이다.
LM35온도 센서는 온도의 변동에 따라 온도값에 해당하는 전압을 출력하여 온도 상태를 알려주는 온도 센서이다. 이것은 1’C당 10mV를 출력해 준다. 즉 측정온도가 24.5’c일 경우라면 245mV가 출력되는 것이다. 우리가 이번 AVR CPU의 A/D내장 기능을 실험하기 위해 이 온도 센서를 사용하는 이유는 온도 값이 전압으로 출력 되므로, 이 출력된 전압을 AVR의 A/D컨버터를 사용하여, 디지털로 바꿔 주면 온도 값을 알수있기 때문이다. 그리고 이렇게 변환된 온도 값을 LCD로 출력해 주면 일상 생활에서 편리하게 사용할 수 있는 디지털 온도계가 되는것이다.
4. LM35DZ 아날로그 온도센서 & LM385-1.2 전압레퍼런스
LM35온도 센서는 위의 그림에서 보듯 다리가 3개인 트랜지스터 모양을 하고 있다.
이것은 아래에서 본 그림이므로 정면에서 보았을 때, 맨 좌측핀이 Vcc전원이고, 가운데 핀 온도 값에 해당하는 출력 전압이 나오는 핀이며, 맨 우측 핀이 GND핀이다.
LM35는 ‘C당 10mV의 출력이 나오므로, 이렇게 출력된 전압을 1mV까지 정확히 읽을수만 있다면, 0.1’C의 정밀도를 가진 디지털 온도계를 만들수 있는것이다.
이런 아날로그 온도센서를 AVR에서 활용하는 방법은 바로 AVR에 내장된 A/D컨버터를 사용하는 것이다. AVR은 아주 우수한 10비트 정밀도를 가진 A/D컨버터를 가지고 있어 아날로그 값을 1024의 단계까지 읽어 낼수 있다.
AVR의 아날로그 컨버터는 모두 8개의 채널을 가지고 있는데, PORTA에 연결되어 있다. 우리는 PORTA의 PA0에 온도센서를 연결하였으며, 좀더 정확한 온도 검출을 위해 레퍼런스 전압을 1V로 맞추어 놓았다. 간단히 아날로그 입력을 연결하는 방법을 아래 그림에서 보여준다.
또한 위 그림에서 기준 전압 용으로 LM385-1.2칩을 사용하는데 이 칩은 매우 정밀한 기준 전압을 출력해 준다.
LM385의 응용 회로는 위에서 보는것과 같다. 다만, 우리는 5V전원 전압을 사용하므로, 저항을 10K옴을 사용하였다.
5. AVR의 A/D컨버터 관련 레지스터들
우선 AVR의 A/D컨버팅 계산은 다음과 같이 계산된다. 여기서 Vref는 우리가 위에서 설정한 1V의 전압값이 될 것이며, Vin은 바로 LM35온도 센서에서 입력되어지는 아날로그 전압이 되는것이다.
이렇게 변환된 디지털 값이 바로 ADCL과 ADCH에 각각 저장되게 된다. AVR의 A/D컨버터는 10비트의 해상도를 가지고 있으므로 ADCL의 8비트와 ADCH의 2비트만이 유효한 값이 된다.
AVR은 8채널의 A/D 입력 라인을 가지고 있는데, 이것들 중 어느것으로 부터 아날로그 값을 받아 올 것인지는 ADMUX레지스터에서 설정해 주게 되며, 레퍼런스 전압을 어떤 종류를 사용할지도 이 레지스터에서 설정해 준다. 우리는 이번 실험에서는 AREF 즉 외부 기준 전압을 사용한다.
아래의 ADCSRA는 AD컨버터 전반에 대한 설정을 해주는데, A/D를 활성화 시키고, 인터럽트를 설정해 주는 등의 역할을 한다.
또한 SFIOR레지스터의 일부 비트들로 A/D컨버터와 관련이 있는데, ADTS2,1,0가 설정에 따라 A/D컨버터에 영향을 주게된다. 우리는 여기서 FreeRunning모드라는 연속적인 A/D컨버팅 모드로 설정해서 온도를 검출 할 것이다.
6. 실험 실습 회로 구성
7. 실험 실습 프로그램 소스
//**************************************************************************************
//
//
//
// 마이크로프로세서 실습 Atmega8535 KEST-B1 @ Kaywon University 2004
//
// byProfessor S.J.
Park
//
// Atmega8535 LM35 Thermosensor interface test ver. 1.0
//
//**************************************************************************************
#include <avr/io.h>
#include <avr/pgmspace.h>
#define TRUE 1
#define FALSE 0
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned int INT16U;
typedef signed int INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
typedef float F32;
typedef double F64;
typedef unsigned char BOOL;
unsigned char __attribute__ ((progmem)) msg1[]=" Kaywon Emb.Dept";
unsigned char __attribute__ ((progmem)) msg2[]=" KEST-B1 rev.A ";
unsigned char __attribute__ ((progmem)) Usermsg1[]=" Digital Thermo ";
unsigned char __attribute__ ((progmem)) Usermsg2[]=" LM35DZ : ";
unsigned char __attribute__ ((progmem)) ASCII[]="0123456789ABCDEF"; // 16진 ASCII 코드
unsigned int result;
unsigned int CC,DD,EE;
unsigned char Thigh,Tlow,bcdh,bcdl,T100,T10,T1,T01;
unsigned char lo_val, hi_val;
//-----------------------------------------------------------------
//
// LED Port 정의
//
#define LED_PORT PORTB
#define LED_DDR DDRB
#define LED0 PB0
#define LED1 PB1
void IO_Init(void)
{
outp(0xFE,DDRA);
outp(0xe3,DDRB);
outp(0xFF,DDRC);
outp(0xF2,DDRD);
UBRRL=25; // 9600bps, 4MHz
UCSRB=0x18;
}
//Delay 함수 들
void UTIL_DelayUS(INT16U wUS)
{
INT16U wDelayLoops;
register INT16U i;
wDelayLoops = (wUS + 3) / 8;
for (i=0; i<wDelayLoops; i++);
}
void UTIL_DelayMS(INT16U wMS)
{
register INT16U i;
for (i=0; i<wMS; i++)
UTIL_DelayUS(2000);
}
//----------------------------------------------
INT8U ReadRS232(void)
{
while(!(UCSRA & ( 1 << RXC)));
return UDR;
}
void SendRS232(INT8U TXdata)
{
while(!(UCSRA & ( 1 << UDRE)));
UDR=TXdata;
}
//----------------------------------------------
#include "LCD4.h"
//----------------------------------------------
int main(void)
{
INT8U i=0,j=0,RxData;
INT8U k=0;
IO_Init();
L_INIT(); // LCD 초기 설정
DISPLAY(); // msg1, msg2 디스플레이
UTIL_DelayMS(2000);
UserRamWrite1();
PORTB=0x01;
UTIL_DelayMS(1000);
PORTB=0x02;
UTIL_DelayMS(1000);
PORTB=0x01;
UTIL_DelayMS(1000);
PORTB=0x02;
UTIL_DelayMS(1000);
PORTB=0x00;
COMMAND(0x01); // LCD clear?
COMMAND(0x02); // 커서를 홈으로 셋
for (i=0; i<16 ; i++){
CHAR_O(PRG_RDB(&Usermsg1[i])); // 데이터를 얻어서 LCD로 데이터 출력
}
// 2라인 데이터 출력
COMMAND(0xc0); // 커서를 라인 2로 셋
for (i=0; i<16 ; i++){
CHAR_O(PRG_RDB(&Usermsg2[i])); // 데이터를 얻어서 LCD로 데이터 출력
}
outp(0, ADMUX);
outp((1<<ADEN )|(1<<ADSC)|(1<<ADATE)|0x04, ADCSRA);
for(;;){
PORTB=0x00;
UTIL_DelayMS(1000);
PORTB=0x02;
//------------------- LM35 Test ------------------
lo_val = inp(ADCL);
hi_val = inp(ADCH);
result = (unsigned int)(0x00ff & hi_val );
result <<= 8;
result |= (unsigned int)(0x00ff & lo_val );
CC=result; // Vref 1V 일때 , 0.1도 단위의 정밀온도계
DD=CC%1000;
EE=CC/1000;
T100=EE;
CC=DD;
DD=CC%100;
EE=CC/100;
T10=EE;
CC=DD;
DD=CC%10;
EE=CC/10;
T1=EE;
T01=DD;
Thigh=T10;
Thigh=(Thigh<<4)|T1;
Tlow=T01<<4;
// 온도 디스플레이
COMMAND(0xc9); // 커서 조정(LCD8.h 참조)
if(T100==0){ CHAR_O(' '); }
else{ CHAR_O(T100 +0x30); }
if(T10==0){ CHAR_O(' '); }
else{ CHAR_O(T10 +0x30); }
CHAR_O(T1 +0x30);
CHAR_O('.');
CHAR_O(T01 +0x30);
CHAR_O(1);
COMMAND(0xd0); // 커서 조정
}
return TRUE;
}
위 소스에서 가장 핵심적인 코드는 다음의 두 줄이다. 이 두 줄의 코드가 AVR의 A/D컨버터 기능을 설정하고 동작 시키는 것이다.
outp(0, ADMUX); // Digital로 변환할 Analog 입력핀을 PA0로 선택하는 명령
outp((1<<ADEN )|(1<<ADSC)|(1<<ADATE)|0x04, ADCSRA);
// A/D컨버터 제어 레지스터인 ADCSRA를 설정하는 코드로
// ADEN은 1로 설정하여, A/D컨버터 기능을 활성화 시킨다.
// ADSC를 1로 설정하여, A/D컨버터의 컨버팅을 시작시킨다.
// ADATE를 1로 설정하여, 자동으로 계속적인 A/D변환을 하도록 한다.
나머지 부분은 앞 장에서 공부한 LCD디스플레이 코드와 거의 유사하다. 새로 추가된 부분 만을 설명 하자면
unsigned char __attribute__ ((progmem)) Usermsg1[]=" Digital Thermo ";
unsigned char __attribute__ ((progmem)) Usermsg2[]=" LM35DZ : ";
온도 값을 표시해 주는 메인 화면을 표시할 문자열을 두 개의 프로그램 메모리 영역에 저장 하였다.
이 두 문자열은 main함수 내의 다음과 같은 코드로 LCD화면 위에 표시되게 된다.
COMMAND(0x02); // 커서를 홈으로 셋, 즉 커서를 윗줄의 1번째 칸으로 가져간다.
for (i=0; i<16 ; i++){
CHAR_O(PRG_RDB(&Usermsg1[i])); // Usermsg1의 문자데이터를 얻어서 LCD로 출력
}
// 2번째 라인 데이터 출력
COMMAND(0xc0); // 커서를 2번째 라인으로 셋
for (i=0; i<16 ; i++){
CHAR_O(PRG_RDB(&Usermsg2[i])); // Usermsg2의 문자데이터를 얻어서 LCD로 출력
}
이로서, 온도를 표시해 주기 위한 코드는 준비되었고, 실제로 온도를 가져와 계산해서 화면에 표시하는 실제 코드에 대해 설명하겠다. 이 코드는 main함수 안의 for()문 내에 위치해 주기적으로 반복하며, A/D컨버터에서 변환된 디지털 값을 읽어와 LCD에 표시할 수 있도록 값을 100,10,1자리의 숫자문자로 분해해 LCD로 보내주는 코드 이다.
//------------------- LM35 Test ------------------
lo_val = inp(ADCL);
hi_val = inp(ADCH);
result = (unsigned int)(0x00ff & hi_val );
result <<= 8;
result |= (unsigned int)(0x00ff & lo_val );
A/D변환된 디지털 온도값은 ADCL과 ADCH에 저장되게 된다. 이때 ADCH는 하위 2비트만이 유효하다, 그 이유는 앞에서도 설명하였듯 Atmega8535의 A/D컨버터는 10비트 이므로 하위8비트와 상위 2비트로 나누어 레지스터에 저장되어 있는데, 상위 레지스터 8비트 값를 읽어와서 하위 2비트만을 사용하면 되는것이다.
위 코드에서는 하위 값을 lo_val에, 상위 값을 hi_val변수에 일단 저장한 후 16비트 변수인 result에 상위 값을 넣고 좌로 8비트 쉬프트 시킨후 하위 값을 ‘OR’연산하면 16비트의 변수 값이 만들어 지게된다.
이 변수 값이 바로 LM35가 나타내는 온도 값 자체인것이다.
그러나 이 온도 값은 16비트 이진수로 되어 있으므로 LCD로 바로 보내면 이상한 깨어진 문자가 화면에서 표시될 뿐이다. 이것을 우리가 쉽게 읽기 위해서는 3자리 혹은 두자리의 숫자로 바꾸어 하나씩 LCD로 전송해표시해 주어야 한다. 그렇게 하기위해 16비트 이진 값을 100단위,10단위,1단위의 3개의 숫자로 바꿔 주는 코드가 필요하다. 다음의 코드에서 그런 연산이 이루어 진다.
CC=result; // result온도 값을 16비트 변수 CC에 넣는다.
DD=CC%1000; // CC를 1000으로 나누어 나머지를 임시로 DD변수에 저장한다.
EE=CC/1000; // CC를 1000나누어 EE에 저장한다.
T100=EE; // EE값을 100자리 온도 문자변수 T100에 저장한다.
CC=DD; // 아까의 나머지 값인 DD를 CC에 대치한다.
DD=CC%100; // CC를 100으로 나누어 나머지를 임시로 DD변수에 저장한다
EE=CC/100; // CC를 100나누어 EE에 저장한다
T10=EE; // EE값을 10자리 온도 문자변수 T10에 저장한다
CC=DD; // 아까의 나머지 값인 DD를 CC에 대치한다
DD=CC%10; // CC를 10으로 나누어 나머지를 임시로 DD변수에 저장한다
EE=CC/10; // CC를 10나누어 EE에 저장한다
T1=EE; // EE값을 1자리 온도 문자변수 T1에 저장한다
T01=DD; // 아까의 나머지 값인 DD를 0.1단위 온도 문자변수 T01에 저장한다
위 코드에서 100단위의 온도 값을 100이 아닌 1000으로 나눈이유는 온도값이 0.1도 단위로 result에 저장되어 있기 때문이다.
// 온도 디스플레이
COMMAND(0xc9); // 커서 위치를 두번째 줄 10번째 자리에 위치시킨다.
if(T100==0){
CHAR_O(' '); } // 만일 100단위 온도 값이 0이면 공문자를 LCD에 쓴다.
else{
CHAR_O(T100 +0x30); } // 100단위 온도 값에 0x30을 더하여 LCD에 쓴다.
// 그이유는 T100에는 실제 100자리 온도 값에 해당하는 0~9
// 까지의 숫자값이 들어 있는데 이 숫자값과 LCD에 표시할 ASCII ‘0’~’9’의 값과는 0x30 즉 , 48의 차이가 있어 이값을 더해주면 숫자0은 문자 ‘0’이 되는것이다.
if(T10==0){
CHAR_O(' '); }
else{
CHAR_O(T10 +0x30); } // 10단위 온도 값에 0x30을 더하여 LCD에 쓴다.
CHAR_O(T1 +0x30); // 1단위 온도 값에 0x30을 더하여 LCD에 쓴다
CHAR_O('.'); // 컴마 ‘.’ 를 화면에 찍어 준다.
CHAR_O(T01 +0x30); // 0.1단위 온도 값에 0x30을 더하여 LCD에 쓴다
CHAR_O(1); // 사용자 정의 문자로 만들어 놓은 특수 문자인 ‘c를 써준다.
COMMAND(0xd0); // 커서를 화면 바깥으로 보내 깜박이는 것을 방지한다.
#.주의 : 이 문서는 교수님이 쓰신책으로 저작권 있는 내용 허락없이 도배 하거나 상용화 하시면 법의 저촉을 받습니다.
출처 : http://cafe.naver.com/allcyber/1272
감사합니다...혹시 다른 질문 생기면 또 올리겠습니다. 감사합니다.^^