본문 바로가기
[Intel] 엣지 AI SW 아카데미/펌웨어 프로그래밍 75hour

[Intel] 엣지 AI SW 아카데미 25일차 (부산상공회의소 인력개발원)

by 0x01 2023. 12. 17.

부제목: 25일차


일정: 2023.11.29(수)

수업 내용: 
 

 - AD Converter 제어

 

목표: 

 - AD Converter 제어

A/D 변환기란 ? 아날로그 신호를 디지털 데이터로 변환


ATmega128의 A/D 변환기 특징
: 10-bit resolution(분해 능)
: 13~260㎲ 변환시간
: 8개의 단극성 입력 채널
  (ADC0~ADC7)을 포트 F로 입력 받아 multi-plexer에 의해 A/D 변환기로 연결
: 10배와 20배 증폭을 갖는 2개 차동(differential) 입력 (ADC1,ADC0 그리고 ADC3, ADC2)

 

 

 

 

Free running mode
: 변환이 완료되면 자동적으로 즉시 새로운 A/D변환이 시작, ADSC 비트는 High상태 유지

 

 

ADC 레지스터

 

ADMUX (ADC Multiplexer Selection Register)

 

[5]bit – ADLAR (ADC Left Adjust Result)
: A/D 변환 결과값을 ADCH와 ADCL에 저장 형식을 지정하는 비트
: ADLAR=0 → 하위부터 10비트에 A/D 변환 결과 저장
: ADLAR=1 → 상위부터 10비트에 A/D 변환 결과 저장

 

[4]~[0]bit - MUX4~ MUX0(Analog Channel and Gain Selection Bits)

 

 

 

[7] ADEN (ADC Enable) : A/D 변환기 enable 비트
: ADEN=1 → A/D 변환기 동작 가능
: ADEN=0 → A/D 변환기 동작 정지, 소비 전력이 감소시킬 수 있음

 

[6] ADSC(ADC Start Conversion) : ADC 변환 시작 비트
: single running 에서 ADSC=1 → A/D 변환이 시작
: free running 모드, ADSC=1 → 첫 번째 A/D 변환 후 자동적으로 A/D 변환이 계속

 

ADEN=1이 된 후, 첫 번째 A/D 변환에서 clkADC가 25clock 필요하고
다음 변환부터는 13clock 이 소요됨

 

[5] ADFR (ADC Free Running Select) : A/D 변환을 연속적으로 수행하는 모드
: ADFR=1 → A/D 변환기는 Free Running 모드로 설정
: ADFR=0 → A/D 변환기는 single running 모드로 설정

 

[4] ADIF(ADC Interrupt Flag) : A/D 변환 완료 인터럽트 flag 비트
: A/D 변환이 완료되면 ADCH, ADCL에 A/D 변환 결과값이 write되고
ADIF=1이 되면서 A/D 변환 완료 인터럽트 발생
: ADIE=1이고 SREG의 I=1이면,
A/D 변환 완료 인터럽트가 처리되고 이 때 ADIF=0으로 clear됨
: ADIF를 강제적으로 clear 시키려면 ADIF=1로 write 시켜야 함

 

[3] ADIE(ADC Interrupt Enable) : A/D 변환 완료 인터럽트 enable 비트

[2]~[0] ADPS2~ADPS0 (ADC Pre-scaler Select Bits) : A/D 변환기에서 사용되는 시스템 clock의 분주비 선택


8. ADC (Analog to Digital Converter)
: 10비트의 A/D 변환 결과값을 저장
: A/D 변환 결과값을 리드할 때 ADCH를 먼저 read해야 함

 

 

A/D 변환 결과는 ADCH와 ADCL에 저장.

 

Differential (차동) 입력의 A/D 변환 결과 값의 범위

 

ADC 초기화
① AREF 전압 선택, A/D 변환 결과 저장 형식 선택, 
A/D 변환 입력채널 설정 (단극성 입력, 차동입력)
→ 사용 레지스터 : ADMUX
② 사용 모드 설정
A/D 변환 enable, 분주비 설정, 
변환모드 설정(단일변환/ 프리러닝), A/D변환 인터럽트 enable, 
A/D변환기 입력 클럭 분주비 설정
→  사용 레지스터 : ADCSRA
③ 전역 인터럽트 enable 레지스터 : SREG(최상위 비트)

 

<단일변환 모드 A/D 변환 동작>
- ADCSRA 레지스터의 ADSC(ADC Start Conversion) 를 1로 하여 A/D 변환 시작
- ADIF = 1(또는 ADSC=0)이 되어 A/D변환이 완료될 때까지 대기
- A/D 변환 결과 읽기(ADCW 또는 ADCH:ADCL)

 

<프리러닝 모드 A/D 변환 동작>
- 사용모드 설정하면서 ADCSRA 레지스터의 ADSC 비트를 1로 하여 A/D 변환 시작
- A/D 변환 결과 (ADCW 또는 ADCH : ADCL)를 읽고 싶을 때 읽기 가능

 

 

 

#include <mega128.h> 
#include <delay.h>

typedef unsigned char u_char;
flash u_char seg_pat[10]= {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

void AD_disp(int); // A/D 변환값 표시

void main(void)
{ 
    int ad_val;
    DDRB = 0xF0; 
    DDRD = 0xF0; 
    DDRG = 0x0F; 
    ADMUX = 0x06; // ADC6 단극성 입력 선택
    ADCSRA = 0x87; // ADEN=1, 16MHz 256분주 -> 125kHz
    delay_ms(5);
    while(1){
        ADCSRA = 0xC7; // ADEN=1, ADSC = 1 변환 시작
        while((ADCSRA & 0x10) == 0); // ADIF=1이 될 때까지
            ad_val = (int)ADCL + ((int)ADCH << 8); // A/D 변환값 읽기
            AD_disp(ad_val); // A/D 변환값 표시
    }
}

void AD_disp(int val) { 
    float fval;
    int ival, buf; 
    u_char N100, N10, N1;
    
    fval = (float)val * 5.0 / 1024.0; // 전압 값으로 변환
    ival = (int)(fval * 100.0 + 0.5); // 반올림 후 정수화, (소수 둘째자리까지) 
    
    N100 = ival / 100; // 정수부 추출
    buf = ival % 100; 
    N10 = buf / 10; // 소수 첫째 자리 추출
    N1 = buf % 10; // 소수 둘째 자리 추출
    
    PORTG = 0b00001000; // PG3=1, 소수 둘째 자리
    PORTD = ((seg_pat[N1] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N1] & 0x70 ) | (PORTB & 0x0F);
    delay_ms(1);
    
    PORTG = 0b00000100; // PG2=1, 소수 첫째 자리
    PORTD = ((seg_pat[N10] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N10] & 0x70 ) | (PORTB & 0x0F); 
    delay_ms(1); 
    
    PORTG = 0b00000010; // PG1=1, 정수부
    PORTD = ((seg_pat[N100] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N100] & 0x70 ) | (PORTB & 0x0F); 
    PORTB = PORTB | 0x80; // DP on(소수점) 
    delay_ms(1);
}

[예제 8-2] Free running, ADC6 입력 전압 7-Segment 표시

 

ADC6 (PF6)에는 가변저항(VR2) 10KΩ이 연결되어 있으며
이 단자를 통해 입력되는 전압(단극성 입력)을
3개의 7-Segment에 소수 두 자리까지 전압 값으로 표시하는 프로그램을 작성
Free running mode.

 

#include <mega128.h> 
#include <delay.h>

typedef unsigned char u_char;
const u_char seg_pat[10]= {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

void AD_disp(int); // A/D 변환값 표시

void main(void)
{ 
    int ad_val;
    
    DDRB = 0xF0; // 포트 B 상위 4비트 출력 설정
    DDRD = 0xF0; // 포트 D 상위 4비트 출력 설정
    DDRG = 0x0F; // 포트 G 하위 4비트 출력 설정
    
    ADMUX = 0x06; // ADC6 단극성 입력 선택
    ADCSRA = 0xE7; // ADEN=1, ADSC=1, ADFR=1, 16MHz, 256분주 -> 125kHz
    delay_ms(5);
    while(1)
    {
        ad_val = (int)ADCL + ((int)ADCH << 8); // A/D값 읽기
        AD_disp(ad_val); // A/D값 표시
        delay_ms(2);
    }
} 

void AD_disp(int val) { 
    float fval;
    int ival, buf; 
    u_char N100, N10, N1;
    
    fval = (float)val * 5.0 / 1023.0; // 전압값으로 변환
    ival = (int)(fval * 100.0 + 0.5); // 반올림 후 정수화
    
    N100 = ival / 100; // 정수부 추출
    buf = ival % 100; 
    N10 = buf / 10; // 소수 첫째 자리 추출
    N1 = buf % 10; // 소수 둘째 자리 추출
    
    PORTG = 0b00001000; // PG3=1, 소수 둘째 자리
    PORTD = ((seg_pat[N1] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N1] & 0x70 ) | (PORTB & 0x0F);
    delay_ms(1);
    
    PORTG = 0b00000100; // PG2=1, 소수 첫째 자리
    PORTD = ((seg_pat[N10] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N10] & 0x70 ) | (PORTB & 0x0F); 
    delay_ms(1); 
    
    PORTG = 0b00000010; // PG1=1, 정수부
    PORTD = ((seg_pat[N100] & 0x0F) << 4) | (PORTD & 0x0F);
    PORTB = (seg_pat[N100] & 0x70 ) | (PORTB & 0x0F); 
    PORTB = PORTB | 0x80; // DP on(소수점) 
    delay_ms(1);
}