2011년9월20일_ARM_AT91SAM7S256 MCU 내장ADC를 사용하여 CdS(광센서)와 써미스터(온도센서)의 아날로그출력을 디지털값을 바꾸어 PC로 전송하고 CLCD에도 표시하자. (+ SAR ADC이론)




● ARM Board와 광센서(또는 온도센서)의 연결

image image 
                           [그림] 회로도                                                                [사진] ARM Board와 센서

광센서(CdS)는 극성이 없으므로 전류의 방향은 신경쓰지 말고 AD7핀과 VCC3.3핀에 연결한다.
광센서는 CdS로 빛감지부가 황화카드뮴이라는 화학물질로 이루어져 있다.
CdS는 전기장판 비슷한 표면에 닿는 빛의 세기가 강하면 저항이 낮아지고, 
                                            빛의 세기가 약하면 저항이 높아진다.

3.3V를 거쳐 ADC채널7번핀(AD7)에 연결된 센서의 저항이 변화하면 흐르는 전류의 양이 변화하여,
ADC내부저항에 걸리는 전압의 크기가 변화한다.



image

먼저 빛이나 열과 같은 자극이 센서에 가해지면, 센서의 저항이 변화하게 되고,
센서에 연결된 전원에 의해 저항의 변화가 전압의 변화로 바뀐다.
이 전압의 변화가 ADC입력단에 가해지면 디지털값으로 변환이 되고 결과 레지스터에 기록이 된다.

센서의 종류는 감지하는 물리/화학량에 따라 여러 가지로 분류되고,
image  image   image
이미지출처: http://devicemart.co.kr

상기와 같은 SRF05초음파센서는 초음파센서소자 외에 신호처리회로가 들어가 있는 센서모듈이라,
사용하기 편하나 설계의 자유가 줄어들고 단가가 높다. 
오른쪽의 센서는 칼라센서로 마찬가지로 센서소자외에 신호처리회로가 들어가 있는 센서모듈이다.

  
imageimage
이미지출처: http://devicemart.co.kr

그러나 상기의 이런 센서들은 모두 센서자체만 있는 다바이스들로 단가가 낮고 설계의 자유도는 높으나,
전문가가 아니면 제대로 사용하기 어렵다. 예를 들어 저항치 얼마에 몇 ℃인가? 몇 lux인가?  애매하다.
하기와 같이 제품설명도 센서모듈에 비하면 매우 간단하게 표시되어 있다.

image

봐도 모르겠다...ㅠㅠ





● 축차비교방식의 ADC 

image

AD7핀으로 입력되는 입력전압의 크기를 측정하기 위해 비교대상인 DAC출력과 비교한다.
10비트 또는 8비트 분해능에 맞춰 ADC입력클록(AT91SAM7S256의 경우 MCK / ((PRESCAL + 1) * 2) )을,
카운터회로가 0부터 카운트하고 카운트값을 DAC입력에 넣으면 그게 해당하는 아날로그 전압이 출력된다.
이 DAC출력전압을 AD7핀으로 입력된 전압과 비교하여 작으면 계속 카운트하고,
같으면 정지하여 그 때의 값을 레지스터에 넣고 A/D변환이 완료되었다는 것을 레지스터에 기록하여 알려준다.
실제 위와 같이 0부터 카운트하지 않을 수 있음.
   최대값부터 할 수도 있고, 이진탐색트리처럼 빨리 결과를 내기 위해 중간값부터 넣을 수 있음.
   중간값 –> 크거나 작거나 같은지 검사 –> 중간값의 중간값 –> 검사 –> ….
   (ADC에 대해 자세히 알고 싶은 사람은 교과서를 보세요. 본 포스트는 정답이 아닌 어떤 하나의 길만 제시함.)

참조URL : http://www.mcublog.co.kr/263  (AT91SAM7S256도 SAR방식)



image 

아날로그값을 디지털값으로 바꾸는 원리에 대해 이해가 잘 안되면 상기의 개념도를 보면 된다.

어렸을 때 많이 가지고 논 나무블록이나 플라스틱블록 또는 종이박스를 측정대상 근처에 쌓으면,
블록 3개와 같은 경우 높이는 3cm라는 것을 쉽게 알 수 있다.
꼭 블록이 아니더라도 줄자의 경우도 자에 새겨진 눈금과 측정대상을 비교하여 길이를 알아낸다.

오른쪽 블록 2개와 측정대상의 비교도를 보면 2개 쌓은 높이보단 높고 3개 쌓은 높이보단 낮다.
이런 높이차이를 오차라고 부른다. (양자화 오차)
아날로그값을 100% 디지털값으로 바꾸는 것은 불가능하고 근사치를 얻는 것은 가능하다.



 

● AT91SAM7S256내부와 ADC 그리고 ADC변환값 취득에 대해

 
image 

//블록다이어그램과 설명추가






● ADC초기화 절차

   1: void ADC_INIT(void)
   2: {
   3:     //PMC_PCER = (1 << ADC);        // ADC장치에 전력 공급
   4:     ADC_CR = (1 << SWRST);        // 소프트웨어 리셋
   5:     ADC_CHER = (1 << ADC_CH7);    // 채널7 활성.
   6:     ADC_MR = (5 << PRESCAL) | (0 << LOWRES);    // MCK/((PRESCAL + 1) *2) = 48Mhz / 12  = 4Mhz
   7:                                             // 10비트 분해능 
   8:     
   9:     return ;
  10: }

//설명추가




● ADC변환과 데이터 획득 절차

   1: ADC_CR = (1 << START);        // A/D변환 시작.
   2: while(0 == (ADC_SR & (1 << EOC7)));    // 채널7 A/D변환 대기 
   3: uiResult = ADC_CDR[ADC_CH7];            // 채널7 데이터 획득

//설명추가




● 전체소스코드 중 ADC와 main.c만 미리보기 
▷ adc.c

   1: #include "adc.h"
   2:  
   3: void ADC_INIT(void)
   4: {
   5:     //PMC_PCER = (1 << ADC);        // ADC장치에 전력 공급
   6:     ADC_CR = (1 << SWRST);        // 소프트웨어 리셋
   7:     ADC_CHER = (1 << ADC_CH7);    // 채널7 활성.
   8:     ADC_MR = (5 << PRESCAL) | (0 << LOWRES);    // MCK/((PRESCAL + 1) *2) = 48Mhz / 12  = 4Mhz
   9:                                             // 10비트 분해능 
  10:     
  11:     return ;
  12: }
  13:  
  14: unsigned char * ADC_RUN7(void)
  15: {
  16:     static unsigned int uiResult;        // 함수호출시마다 변수를 만들면 속도가 느려짐. static으로 만듬.
  17:     static unsigned char ucBuf[] = "0000\n\r";    //10비트 분해능이니 4자리
  18:                                         //지역변수를 반환하면 안되니 static
  19:  
  20:     //여기서부터 필터에 필요한 변수선언
  21:     static unsigned int uiAverageOld = 0;
  22:     static unsigned int uiAverageNew = 0;
  23:     static unsigned int uiCntElement = 0;
  24:  
  25:     static unsigned int uiData[32] = {0, };
  26:     static unsigned int iCnt;
  27:     static unsigned char ucIN = 0;
  28:     static unsigned char ucOld = 0;
  29:     
  30:                                         
  31:     ADC_CR = (1 << START);        // A/D변환 시작.
  32:     while(0 == (ADC_SR & (1 << EOC7)));    // 채널7 A/D변환 대기 
  33:     uiResult = ADC_CDR[ADC_CH7];            // 채널7 데이터 획득
  34: /*
  35:     // 여기다 평균을 구하는 함수를 만들자.
  36:     // 2. 이동평균필터
  37:     uiData[ucIN] = uiResult;
  38:     ++ucIN;
  39:     if((sizeof(ucBuf) / sizeof(unsigned int))
  40:     uiAverageNew = uiAverageOld 
  41: + (uiResult -uiData[ucOld]) / (sizeof(uiData) / sizeof(unsigned int));
  42:     
  43: 
  44:     // 1. 재귀필터
  45:     //++uiCntElement;
  46:     //uiAverageNew = ((uiCntElement - 1.0) / (float)uiCntElement) * uiAverageOld + ((1.0 / uiCntElement) * uiResult);
  47:     //uiAverageOld = uiAverageNew;
  48:     
  49: 
  50:     ucBuf[0] = '0' + (uiAverageNew % 10000) / 1000;    // 천자리
  51:     ucBuf[1] = '0' + (uiAverageNew % 1000) / 100;        // 백자리
  52:     ucBuf[2] = '0' + (uiAverageNew % 100) / 10;        // 십자리
  53:     ucBuf[3] = '0' + (uiAverageNew % 10);            // 일자리
  54: 
  55: */
  56:     
  57:     ucBuf[0] = '0' + (uiResult % 10000) / 1000;    // 천자리
  58:     ucBuf[1] = '0' + (uiResult % 1000) / 100;        // 백자리
  59:     ucBuf[2] = '0' + (uiResult % 100) / 10;        // 십자리
  60:     ucBuf[3] = '0' + (uiResult % 10);                // 일자리
  61:  
  62:     return ucBuf;
  63: }
  64:  


▷ main.c

   1: #include "pio.h"
   2: #include "dbgu.h"
   3: #include "lcd.h"
   4: #include "led.h"
   5: #include "aic.h"
   6: #include "timer.h"
   7: #include "ultra.h"
   8: #include "adc.h"
   9:  
  10: #define    POSITION(x,y)        ((0x40 * y + x) | 0x80)
  11:  
  12: void ALL_INIT()
  13: {
  14:     LCD_INIT();
  15:     LED_INIT();
  16:     DBGU_INIT();
  17:     ADC_INIT();
  18:     //AIC_INIT();
  19:     //TIMER0_INIT();
  20:     //Ultra_INIT();
  21: }
  22:  
  23: int main(void)
  24: {
  25:     volatile unsigned int iCnt;
  26:  
  27:     ALL_INIT();
  28:     Send_String("test ADC...\n\r");
  29:     LCD_String("test ADC...");
  30:     
  31:     while(1)
  32:     {
  33:         unsigned char *ucP;
  34:  
  35:         for(iCnt = 0 ; 1000000 > iCnt ; ++iCnt);
  36:     
  37:         //Recv_Char();
  38:  
  39:         ucP = ADC_RUN7();
  40:         Send_String(ucP);
  41:         LCD_Inst(POSITION(0,1));
  42:         LCD_String(ucP);
  43:     }
  44:     
  45:     return 0;
  46: }
  47:  


전체소스코드다운로드 -> 20110920_ADC.zip






● 첨부사진과 영상


DSCN4426DSCN4428

광센서(CdS)를 ARM Board에 연결한 모습...
전기장판모양의 센서가 인상적이다.



DSCN4417 DSCN4418

VCC3.3 – CdS – AD7(ARM) 이렇게 연결 후..
센서를 개방시켜 빛을 최대한 받을 수 있도록 한 상태에서는 900이상의 값을 출력하고,
손으로 최대한 가리면 600정도의 값이 나온다.
AD변환값이 매우 출렁거려 울렁거린다. 잡음이 심한가 보다.
(내 생각엔 회로가 잘 못 되었다.)



필터없이 측정한 영상

평균필터를 적용한 영상




DSCN4419 DSCN4420 

그래서 VCC3.3 – CdS – AD7(ARM) – 써미스터 – GND 이렇게 연결한 후 측정해 보니..
값이 비교적 안정적으로 출력되고 대신 전체적으로 값이 500정도 낮게 출력되었다.
써미스터로 분압한 이유는 저항이 없어서이고 온도는 빛에 비해 느리게 변화하니 간단히 테스트하는데는 문제가 없다고 본다.

필터없이 측정한 영상



평균필터적용한 영상




DSCN4429 DSCN4432 DSCN4434

써미스터는 제대로 실험하지 못 하였음 ㅠㅠ