Atmega128 기초 – ADC(Analog to Digital) 변환
Atmega128의 ADC에 대해 다룹니다.
2016. 6. 24 최초작성
센서들은 보통 아날로그 출력 값들을 내놓기 때문에 ADC(Analog to Digital Converter)를 거쳐 마이크로컨트롤러에 연결하는 것이 필요하다. 이를 위해 Atmega128에는 다음과 같이 포트 F에 8개의 핀을 제공하고 있다. Atmega 128의 ADC는 연속 근사(successive approximation) 방식을 이용하여 아날로그 입력 전압을 10비트 디지털 값으로 변환한다.
아날로그 입력 채널과 Gain은 ADMUX 레지스터의 MUX 비트들을 설정함으로써 선택된다. Atmega128에는 8개의 ADC 채널(PF0~PF7)이 있다. 이 중 어떤 것을 사용할지 정할 때 다음 비트들을 사용한다.
기준 전압을 선택하기 위해 사용한다. ADC가 동작하기 위해서는 기준전압이 필요하다. 이를 위해 세 개의 핀이 있는데 AREF, AVCC, GND이다. 위 표에서 첫번째 옵션은 외부에서 기준 전압을 공급해주는 것으로 AREF핀에 공급해주면 된다. 두번째 옵션은 AVCC핀으로부터 공급되는 전압을 사용한다. 네번째 옵션은 내부 전압 2.56V를 사용하는 것이다.
ADCSRA레지스터의 ADEN비트를 1로 세팅하면 ADC가 활성화 된다. ADEN이 1로 설정되기 전까지는 전압 기준(Voltage reference)과 입력 채널 선택은 영향을 주지 못한다. 이 비트와 SREG의 I비트가 1로 설정되어야 ADC의 완료 인터럽트가 활성화 된다.
변환이 완료되어 데이터 레지스터가 업데이트되면 ADCSRA레지스터의 ADIF 비트가 자동으로 1로 설정된다. 따라서 변환이 완료되었는지 체크할 때 이 비트를 사용하면 된다.
ADC의 변환 결과가 두 개의 ADCH, ADCL레지스터에 나누어 저장된다. ADC는 10비트 resolution을 가지기 때문에 변환결과를 저장하기 위해 10비트가 필요하기 때문이다. ADMUX 레지스터의 ADLAR 비트 설정 값에 따라 결과의 정렬 방법이 바뀐다.
ADCL 레지스터를 읽고 그다음 ADCH를 읽어야 한다.
ADCL 레지스터에서 데이터를 읽으면, ADC가 데이터 레지스터에 접근할 수 없도록 블록된다. ADCH 레지스터 값까지 모두 읽고나야 다시 ADC가 데이터 레지스터에 접근할 수 있게 된다.
변환이 완료되면 ADC에서 인터럽트가 발생하게 되어 있다.
Single Conversion 모드에서는 ADCSRA 레지스터의 ADSC비트가 1로 설정되면 ADC 변환이 시작된다.
변환이 완료되면 하드웨어에 의해서 ADSC 비트의 값은 0으로 클리어 된다. 따라서 변환이 완료되었는지 체크할 때 이 비트를 사용하면 된다. 변환하는 도중에 다른 채널을 선택하게 되면 현재 변환이 완료된 후에 채널이 변환되게 된다.
Free Running 모드에서는 연속적으로 샘플링과 ADC 데이터 레지스터로의 업데이트가 이루어지게 된다. ADCSRA레지스터의 ADFR 비트를 1로 설정하면 Free Running 모드가 선택된다.
ADCSRA레지스터의 ADSC 비트를 1로 설정해야 첫 번째 변환이 시작된다. 이 모드는 ADC인터럽트 플래그 ADIF가 설정 여부와는 상관없이 독립적으로 연속적으로 변환을 수행한다.
AVR의 ADC는 아날로그 신호를 디지털 신호로 변환시 일정한 간격을 갖도록해서 변환하는데 이를 결정하는 것이 클록 주파수이다. 일반적으로 ADC는 50KHz ~ 200KHz사이의 범위에서 동작한다. 여기서 문제는 CPU 클록 주파수가 MHz 단위이기 때문에 값이 너무 크다. 따라서 주파수를 나누어서 써야 한다. 이때 prescaler 값에 따라 필요한 주파수를 얻을 수 있다. prescaler값은 2, 4, 8, 16, 32, 64, 128이 될 수 있다
prescaler로 64를 사용한다면 F_CPU/64가 ADC를 위한 주파수값이 된다. 예를 들어 시피유 클록 주파수가 F_CPU = 16MHz이라면 ADC를 위한 주파수는 16M/64 = 250kHz가 된다.
변환이 완료되면 결과는 ADCL과 ADCH레지스터에 저장된다.
선택된 핀의 입력 전압 VIN, 전압 기준 VREF에 대해서 ADC 결과는 다음처럼 계산된다.
5V 기준 전압을 사용한다면 아날로그 값은 0~5V 사이의 아날로그 값을 가지게 되며 ADC를 거치면서 2^10=1024 단계의 값으로 디지털화 된다. 따라서 0V 아날로그 전압은 0이되며 5V아날로그 전압은 1023이 되며 2.5V는 512가 된다.
cds를 아래처럼 연결하고 프로그래밍 코드 태스트를 한다.
/* * ADC.c * * Created: 2015-03-03 오전 12:37:57 * Author: webnautes */ #define F_CPU 16000000UL //1. baud rate를 선택 #define USART_BAUDRATE 9600 //2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다. #define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) #include <avr/io.h> #include <stdio.h> void adcInit(void) //ADC 초기화 함수 { ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz ADMUX |= (1<<REFS0); //AVCC(5V) ADCSRA |= (1<<ADEN); //ADC 인에이블 } uint16_t readAdc(uint8_t channel) //값을 읽어오는 함수 { ADMUX &= 0xF0; ADMUX |= channel; ADCSRA |= (1<<ADSC); //변환 시작 while(ADCSRA&(1<<ADSC));//변환 완료되기를 기다림. return ADCW; //ADC값 반환 } void usartInit() { //3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다. UBRR0H = (uint8_t)(UBRR_VALUE>>8); UBRR0L = (uint8_t) UBRR_VALUE; //4. USART 설정 UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트 UCSR0C &= ~(1<<USBS0); //stop bit : 1비트 UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode //5. 송수신을 가능하게 한다. UCSR0B=(1<<RXEN0)|(1<<TXEN0); } void uart_putchar(uint8_t u8Data, FILE *stream ) { //이전 전송이 끝나기를 기다림 while(!(UCSR0A&(1<<UDRE0))){}; UDR0 = u8Data; } uint8_t uart_getchar( FILE *stream) { // 수신 되기를 기다림 while(!(UCSR0A&(1<<RXC0))){}; return UDR0; } //http://www.appelsiini.net/2011/simple-usart-with-avr-libc FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ); int main(void) { usartInit(); stdout = &uart_output; stdin = &uart_input; adcInit(); while(1) { int value = readAdc(0); printf( "%d \r\n", value ); } } |