ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Atmega128 기초 - ADC(Analog to Digital) 변환
    Avr/Atmega128 강좌 2016. 6. 24. 19:35


    센서들은 보통 아날로그 출력 값들을 내놓기 때문에 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를 아래처럼 연결하고 프로그래밍 코드 태스트를 한다.


    1. /* 
    2.  * ADC.c 
    3.  * 
    4.  * Created: 2015-03-03 오전 12:37:57 
    5.  *  Author: webnautes 
    6.  */   
    7.     
    8.     
    9. #define F_CPU 16000000UL  
    10.     
    11. //1. baud rate 선택  
    12. #define USART_BAUDRATE 9600  
    13.     
    14. //2.시스템 클록과 원하는 baud rate 이용하여 UBRR 값을 계산한다.  
    15. #define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)  
    16.     
    17.     
    18. #include <avr/io.h>  
    19. #include <stdio.h>  
    20.     
    21.     
    22.     
    23. void adcInit(void)  //ADC 기화 함수
    24. {  
    25.     ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz  
    26.     ADMUX |= (1<<REFS0);       //AVCC(5V)  
    27.     ADCSRA |= (1<<ADEN);      //ADC 인에이블  
    28. }  
    29.     
    30. uint16_t readAdc(uint8_t channel)  //값을 읽어오는 함수
    31. {  
    32.         
    33.     ADMUX &= 0xF0;  
    34.     ADMUX |= channel;  
    35.         
    36.     ADCSRA |= (1<<ADSC);      //변환 시작  
    37.     while(ADCSRA&(1<<ADSC));//변환 완료되기를 기다림.  
    38.         
    39.     return ADCW;  //ADC 반환  
    40. }  
    41.     
    42.     
    43. void usartInit()  
    44. {  
    45.     //3. UBRR0 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.  
    46.     UBRR0H = (uint8_t)(UBRR_VALUE>>8);  
    47.     UBRR0L = (uint8_t) UBRR_VALUE;  
    48.     
    49.     //4. USART 설정  
    50.     UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트  
    51.     UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트  
    52.     UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode  
    53.         
    54.     //5. 송수신을 가능하게 한다.  
    55.     UCSR0B=(1<<RXEN0)|(1<<TXEN0);  
    56. }  
    57.     
    58.     
    59. void uart_putchar(uint8_t u8Data, FILE *stream )  
    60. {  
    61.     //이전 전송이 끝나기를 기다림  
    62.     while(!(UCSR0A&(1<<UDRE0))){};  
    63.         
    64.     UDR0 = u8Data;  
    65. }  
    66.     
    67. uint8_t uart_getchar( FILE *stream)  
    68. {  
    69.     // 수신 되기를 기다림  
    70.     while(!(UCSR0A&(1<<RXC0))){};  
    71.         
    72.     return UDR0;  
    73. }  
    74.     
    75. //http://www.appelsiini.net/2011/simple-usart-with-avr-libc  
    76. FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);  
    77. FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ);  
    78.     
    79. int main(void)  
    80. {  
    81.     usartInit();  
    82.     stdout = &uart_output;  
    83.     stdin  = &uart_input;  
    84.             
    85.     adcInit();  
    86.             
    87.         
    88.     while(1)  
    89.     {  
    90.         int value =  readAdc(0);  
    91.         printf( "%d \r\n", value );  
    92.     }  
    93. }  


    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    TAG

    댓글 4

    • 고기범 2017.02.02 17:40


      readadc함수에서 return 값 ADCW는 어디서 정의된 변수인가요? 레지스터 비트도 아니고 따로 정의된 변수도 아닌 것 같은데요...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.02.02 22:11 신고


        ADCW는 데이터시트에는 없고.. 컴파일러에서 편의상 정의해준겁니다.. 한번에 ADCL과 ADCH를 가져올수 있습니다.

        iom128.h 헤더파일을 열어보면..
        ADCW가 16비트로 정의되어있는데 주소가 ADCL 주소입니다.

        그런데 ADCL과 ADCH는 모두 8비트 레지스터이고 주소가 인접해 있어서... ADCW를 통해 두 레지스터값을 모두 가져올 수 있게됩니다..

        /* ADC Data Register */
        #define ADCW _SFR_IO16(0x04)
        #ifndef __ASSEMBLER__
        #define ADC _SFR_IO16(0x04)
        #endif
        #define ADCL _SFR_IO8(0x04)
        #define ADCH _SFR_IO8(0x05)

    • 신해동 2019.11.25 18:13


      좋은 글 감사합니다 궁금한게 있는데
      readAdc에서 매개변수로 받는 channel이 ADC포트 번호가 맞나요?
      만약 0번포트에 cds 연결하고 1번포트에 다른 센서를 연결하면 main함수에서 readAdc(0)이 아니라 readAdc(1)로 하면 1번포트에 연결된 센서의 값을 읽게되나요?

Designed by Tistory.