이 블로그는 임베디드 컴퓨터를 이용한 장치(시스템) 개발과 원격제어에 필요한 지식을 공유 하기 위한 블로그 입니다.
실제 개발과 프로그램 예를 위하여 Microchip 사의 ATmega128를 사용한 보드와 Arduino Mega 보드(ATmega2560), Raspberry Pi, Raspberry Pi Pico, WiFi 모듈을 사용 합니다.

avr-sensors-modules

AVR(Atmega128) 센서(Sensors)와 센서 모듈(Sensor Modules)
AVR(Atmega128) 센서(Sensors)와 센서 모듈(Sensor Modules)


  • 센서에 입력되는 신호가 임계값을 넘으면 Low/High를 출력하는 센서 모듈
    • Low/High를 출력하는 센서 모듈 예
    • 아래 사진은 자주 자용하는 Low/High를 출력하는 센서 모듈의 예 이다. 아래 예의 센서는 입력 신호가 미리 정해진 임계치를 넘으면 Low 상태에서 High 상태(or High 상태에서 Low 상태)로 천이(Rising edge or Falling edge)하기 때문에 External Interrupt 처리 방식으로 제어 할 수 있다.

      PIR 센서모듈(HC-SR501)

      Sound 센서모듈(SZH-EK033)

      홀 센서(WSH131)


    • Low/High를 출력하는 센서 모듈을 사용하는 구성도
    • Low/High를 출력하는 센서 모듈을 제어하는 프로그램 예
      • 윗 예의 센서 모듈은 입력 신호가 임계치를 넘기면 Low 상태에서 High 상태(or High 상태에서 Low 상태)로 천이하기 때문에 Input port의 값(또는 변동)을 이용하여 센서의 상태를 입력 받을 수 있다.

        • Input port의 값을 이용하는 예
          • PIR 센서모듈(HC-SR501)은 제어 기능(인체가 감지되면 일정시간 동안 High 상태를 유지하는 기능, High 상태에서 Low 상태로 천이하면 일정시간 신호를 감지하지 않는 기능 등)을 내장하고 있기 때문에 마이크로 콘트롤러를 사용하지 않아도 대부분의 용도에 사용할 수 있다. 아래 프로그램은 PIR 센서모듈의 출력을 LED에 보여 주는 간단한 프로그램 예 이다.

          • PIR 센서모듈(HC-SR501)의 상태를 LED에 표시하는 프로그램 예(Atmel Studio 7): sensor_handler.zip
          • 
            // Target : Atmega128
            // Crystal: 16Mhz
            
            #include <avr/io.h>
            #include <avr/interrupt.h>
            #include <stdio.h>
            
            void port_init(void)
            {
            	// Sensor의 출력신호를 PE4에 연결 하고 다른 한 단자는 GND에 연결한다.
            	DDRE &= (~0x10);	// PORTE의 PE4를 Input Port로 설정
            	PORTF = 0x00;		// PORTF의 초기값 설정(PORTF의 LED가 꺼진 상태)
            	DDRF  = 0xff;		// PORTF를 Output Port로 설정
            }
            
            // Initialize all peripherals
            void init_devices(void)
            {
            	cli();         // Disable all interrupts
            	port_init();   // I/O Port init
            	sei();         // Re-enable interrupts
            }
            
            int main (void)
            {
            	init_devices();
            	
            	// Infinite loop
            	while(1){
            		// PE4 가 Low인 경우
            		if((PINE & 0x10) == 0){
            			PORTF &= ~0x01;		// LED(PF0): Turn off
            		}
            		// PE4 가 High인 경우
            		else {
            			PORTF |= 0x01;		// LED(PF0): Turn on
            		}
            	}
            	return 0;
            }
                      
          • 실험을 위한 회로 구성
            • Atmega128 개발보드와 센서 모듈 연결
              • Atmega128 개발보드 PE4 <-> 센서의 Output pin
              • Atmega128 개발보드 VDD(5V) <-> 센서의 Power pin.
              • Atmega128 개발보드 GND <-> 센서 GND
            • Output port(PF0)에 LED 연결
              • PF0에 저항(330Ω)과 LED를 직열로 연결한다.
          • 실험 방법
            • sensor_handler.zip 파일을 Download하여 압축을 해제한다.
            • sensor_handler.hex를 개발 보드에 Upload 한다.
            • 실험:
              • 개발 보드의 프로그램(sensor_handler)을 실행한다.
              • Atmega128 개발보드의 프로그램을 실행하고 PIR(인체 감지) 센서모듈(HC-SR501) 앞에 손(인체)을 가까이 접근 시킨다.
              • 센서모듈(HC-SR501)이 인체를 감지하면 일정 시간(센서 모듈에서 설정된 시간) 동안 LED가 Turn on 상태를 유지한다.
        • Input port 값의 변동(External interrupt 방식)을 이용하는 예
          • Sound 센서와 홀 센서는 입력 신호의 크기가 일정한 값을 초과하면 출력이 High 상태에서 Low 상태로 천이한다. 아래 예는 입력 신호의 크기가 일정한 값을 초과하면 일정 시간 동안(4초) LED가 Turn on 되는 프로그램 예이다.

          • Sound 센서와 홀 센서 응용을 위한 프로그램 예(Atmel Studio 7): sensor_event_one_short.zip
          • 
            // Sound 센서 모듈(SZH-EK-33), 홀센서(WSH131)
            // 입력 신호가 일정 기준을 초과한 경우(센서 High -> Low) LED를 4초동안 Turn on 한다.
            // PE4(외부 Interrupt 4번)를 이용 하여 High/Low를 출력하는 센서(Sensor) 신호를 받는다.
            // 4초 Delay을 구현하가 위하여 Timer1의 Compare Match A Interrupt를 이용한다.
            
            // Target : Atmega128
            // Crystal: 16Mhz
            #include <avr/io.h>
            #include <avr/interrupt.h>
            #include <stdio.h>
            
            void port_init(void)
            {
            	// PE4(INT4)를 이용 하여 Sensor의 출력신호(High/Low)를 입력 받는다.
            	// Sensor의 출력신호를 PE4(External Interrupt)에 연결 하고 다른 한 단자는 GND에 연결 하여야 한다.
            	DDRE &= (~0x10);	// PORTE의 PE4를 Input Port로 설정
            
            	PORTF = 0x00;		// PORTF의 초기값 설정(PORTF의 LED가 꺼진 상태)
            	DDRF  = 0xff;		// PORTF를 Output Port로 설정
            
            	// External Interrupt 4, Falling edge interrupt.
            	EICRB = 0x02;
            	// External Interrupt 4, Rising edge interrupt.
            	//EICRB = 0x03;
            	// External Interrupt 4 enable
            	EIMSK |= (1 << INT4);
            }
            
            // Timer1의 Output Compare Interrupt A를 이용하여 4Sec Time delay를 구현한다.
            #define DELAY_SEC (62500)    //  64uSec * 62500 = 4000000uSec = 4Sec
            void timer_init(void)
            {
            	// (0 << WGM13) | (0 << WGM12)(0 << WGM11) | (0 << WGM10) : Normal port operation.
            	// (1 << CS12) | (0 << CS11) | (1 << CS10) : 101  prescale clk/1024, Clock 주기 : 64uSec
            	TCCR1B = ((1 << CS12) | (0 << CS11) | (1 << CS10));
            
            	// 이 예에서는 Output Compare Interrupt A를 사용하기 때문에 4Sec 시간 지연을 얻기 위하여는
            	// OCR1A의 값을 TCNT1 + DELAY_SEC로 설정하여야 한다.
            	// OCR1A = TCNT1 + DELAY_SEC;
            	TIMSK &= ~(1 << OCIE1A);     // Output Compare A Match Interrupt Disable
            }
            
            // Initialize all peripherals
            void init_devices(void)
            {
            	cli();         // Disable all interrupts
            	port_init();   // I/O Port initialized
            	timer_init();  // Timer1 initialized
            	sei();         // Re-enable interrupts
            }
            
            // External Interrupt 4가 발생 하였을 때 실행되는 함수
            SIGNAL (INT4_vect)
            {
            	EIMSK &= ~(1 << INT4);  // External Interrupt 4 Disable
            
            	PORTF |= 0x01;			// LED(PF0): Turn on
            
            	OCR1A = TCNT1 + DELAY_SEC;	//4 Sec Delay(Timer1 output compare A match)
            	TIMSK |= (1 << OCIE1A);		// Output Compare A Match Interrupt Enable
            }
            
            // Timer1 Output Compare A Match Interrupt가 발생 하였을 때 실행되는 함수
            SIGNAL (TIMER1_COMPA_vect)
            {
            	TIMSK &= ~(1 << OCIE1A);	// Output Compare A Match Interrupt Disable
            	PORTF &= ~0x01;				// LED(PF0): Turn off
            
            	EIFR |= (1 << INTF4);       // Clear External Interrupt Flag
            	EIMSK |= (1 << INT4);		// External Interrupt 4 enable
            }
            
            
            int main (void)
            {
            	init_devices();
            	
            	// Infinite loop
            	while(1){
            
            	}
            	return 0;
            }
                      
          • 실험을 위한 회로 구성
            • Atmega128 개발보드와 센서 모듈 연결
              • Atmega128 개발보드 PE4 <-> 센서의 Output pin
              • Atmega128 개발보드 VDD(5V) <-> 센서의 Power pin.
              • Atmega128 개발보드 GND <-> 센서 GND
              • 홀 센서인 경우에는 전원과 센서의 Output pin 사이에 약 4.7K 부하 저항을 연결한다.
            • Output port(PF0)에 LED 연결
              • PF0에 저항(330Ω)과 LED를 직열로 연결한다.
          • 실험 방법
            • sensor_event_one_short.zip 파일을 Download하여 압축을 해제한다.
            • sensor_event_one_short.hex를 개발 보드에 Upload 한다.
            • 실험:
              • Atmega128 개발보드의 프로그램을 실행한다.
              • 프로그램이 실행되면 센서에 임계치 이상의 신호를 인가 한다.
              • 센서에 임계치 이상의 신호를 인가되면 LED가 지정된 시간(4Sec) 동안 Turn on 된다.
            • 센서에 임계치 이상의 신호를 인가하는 방법 예
              • Sound 센서모듈(SZH-EK033): 센서에 큰 소음이나 진동(입으로 후하고 마이크에 진동)을 가한다.
              • 홀 센서(Hall Sensor) WSH131: 먼저 홀 센서 회로를 구성하고 홀 센서 앞에 자성체(자석)를 접근 시킨다.
              • 참고자료: 홀 센서 회로 예


  • Analog 전압을 출력하는 센서 모듈
    • Analog 전압을 출력하는 센서 모듈 예
    • 아래 사진은 자주 자용하는 Analog 전압을 출력하는 센서 모듈의 예 이다. 아래 예 센서는 입력 신호의 크기에 따라 출력 전압이 변동하기 때문에 센서 모듈의 출력 전압을 디지털 값으로 변환하는 A/D 변환기를 사용하여야 한다.

      수위 센서(Water Sensor)

      포토 센서(GL5516)

      온도 센서(LM35DZ)


    • Analog voltage를 출력하는 센서 모듈을 사용하는 구성도
    • Analog voltage를 출력하는 센서 모듈을 사용하는 프로그램 예
      • 윗 예의 센서 모듈은 모두 Analog voltage 신호를 출력하기 때문에 ADC를 사용하여야 한다.

      • Atmega128 개발보드를 사용하여 Analog voltage를 주기적으로 측정하여 출력하는 프로그램 예(Atmel Studio 7): sensor_analog_interrupt_uart.zip
      • 아래 프로그램 예는 sensor_analog_interrupt_uart 프로그램 중 Timer를 이용하여 측정 주기를 발생하고 ADC로 Analog voltage를 측정하는 프로그램 예이다. 전체 프로그램은 sensor_analog_interrupt_uart.zip에 포함되어 있다.

        
        #include <avr/io.h>
        #include <avr/interrupt.h>
        #include <stdio.h>
        
        #include "gnu_sciutil.h"
        #include "adc.h"
        
        unsigned short int adcResult;
        
        void init_port(void)
        {
        	DDRF &= ~(1 << PF0);	// PF0 : Input Port
        }
        
        #define ADC_PERIOD 1562		// 1562 * 64uSec = 0.1Sec
        void timer3_init(void)
        {
        	// Timer3를 Normal Mode로 설정 한다.
        	// ADC 변환 주기: OC3A Rg의 Compare match Interrupt를 이용 하여 0.1 Sec 주기의 
        	//  Interrupt를 발생 시키고, 이 주기를 ADC 변환 주기로 이용 한다.
        	// (0 << WGM33) | (0 << WGM32)  | (0 << WGM31) | (0 << WGM30) : Normal mode
        	// (0 << COM3A1) | (0 << COM3A0) : Normal port operation, OCnA disconnected.
        	TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (0 << WGM31) | (0 << WGM30);  
        	// (1 << CS32) | (0 << CS31) | (1 << CS30) : 101  prescale clk/1024, Clock 주기 : 64uSec
        	TCCR3B = (0 << WGM33) | (0 << WGM32) | (1 << CS32) | (0 << CS31) | (1 << CS30);  
        
        	TCNT3 = 0x0000;			// Timer3 Counter Register 초기화
        	// OCR3A 값 설정 : 1562 * 64uSec = 0.1Sec
        	OCR3A = TCNT3 + ADC_PERIOD;
        
        	// Timer/Counter3, Output Compare A Match(ADC 변환 Start) Interrupt Enable
        	ETIMSK |= (1 << OCIE3A);
        }
        
        void adc_init(void){
        //	(0 << REFS1) | (0 << REFS0) ; AREF, Internal Vref turned off
        //	(0 << ADLAR)  ; ADLAR: ADC Right Adjust Result
        //	MUX4:0 <= 00000 ;  ADC0, Single Ended Input
        	ADMUX = 0x00;	// Ch 0 사용
        //	(1 << ADEN)  ; ADC Enable
        //	(0 << ADSC)  ; ADC Start Conversion, 0 : Stop Convertion , 1 Start Convertion 
        //	(0 << ADFR)  ; Single Convertion mode
        //	(1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) ; Prescaler Select Bits, Division Factor ; 128
        //     ADC의 Clock frequency: 16M /128 = 125KHz
        //	(0 << ADIE)  ; ADC Interrupt Disable
        	ADCSRA = (1 << ADEN) | (0 << ADSC) | (0 << ADFR) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (0 << ADIE);
        }
        
        void init_adc(void){
        	init_port();
        	timer3_init();
        	adc_init();
        }
        
        SIGNAL(TIMER3_COMPA_vect)
        { 
        	ETIMSK &= ~(1 << OCIE3A);      // Timer3, Output Compare A Match Interrupt Disable
        	OCR3A = TCNT3 + ADC_PERIOD;    // Output Compare A Match Interrupt 주기 설정
            // A/D 변환
        	ADCSRA |= (1 << ADSC)  ;       // ADC Start Conversion
        	ADCSRA |= (1 << ADIE);         // ADC Interrupt Ensable
        	ETIMSK |= (1 << OCIE3A);       // Timer3, Output Compare A Match Interrupt Enable
        }
        
        SIGNAL(ADC_vect)
        { 
        	ADCSRA &= ~(1 << ADIE);        // ADC Interrupt Disable
        	// ADC의 출력을 부호없는 10진수로 출력한다.
        	// 센서의 종류에 따라 출력 값을 조정하는 코드가 필요하다.
        	adcResult = ADC;
        	// A/D 변환 결과를 모니터에 출력 한다.
        	SCI_OutChar(CR);
        	SCI_OutString("    ");
        	SCI_OutChar(CR);
        	// ADC의 출력을 부호 없는 10진수로 출력한다.
        	// 센서의 종류에 따라 출력 값을 조정하는 프로그램이 필요하다.
        	SCI_OutUDec(adcResult);
        }
                
      • 실험을 위한 회로 구성
        • PC와 AVR 사이에 UART 통신선을 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 RXD와 Atmega128의 TXD0(PE1)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 TXD와 Atmega128의 RXD0(PE0)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 GND와 Atmega128의 GND를 연결한다.
          • 주: 컴퓨터에 Serial Port가 없는 경우 "USB --> UART(RS232) 변환 모듈"를 사용(USB Port를 Serial Port로 변환)하여 연결한다. 개발보드에 따라서는 "USB --> UART(RS232) 변환 모듈"을 내장하고 있는 경우가 많기 때문에 본인이 사용하는 개발보드에서 확인 하여야 한다.

            참고자료: USB - Serial 변환 모듈

        • Atmega128 개발보드와 센서 모듈 연결
          • Atmega128 개발보드 ADC0를 입력 단자(PF0) <-> 센서의 Analog output pin
          • Atmega128 개발보드 VDD(5V} <-> 센서 Vdd pin(센서의 종류에 따라 공급 전압을 확인 할 것)
          • Atmega128 개발보드 GND <-> 센서 GND
      • 실험 방법
        • sensor_analog_interrupt_uart.zip 파일을 Download하여 압축을 해제한다.
        • sensor_analog_interrupt_uart.hex를 개발 보드에 Upload 한다.
        • 모니터 프로그램(예: OC-Console, Tera Term)을 설치(이미 설치되어 있는 경우에는 실행만 하면됨)하고 실행 한다.
        • 모니터 프로그램에서 필요한 설정(Serial Port와 Baudrate(19200) 등을 설정)을 한다.
        • 실험:
          • 개발 보드의 Reset SW를 누르면 모니터에 "ADC Testing." 메세지가 출력된다.
          • 0.1 Sec 주기로 ADC0(PF0)의 전압 값이 A/D 변환되어 모니터에 출력된다.
          • 센서에 신호(센서의 종류에 따라)를 가하고 Console에 출력되는 측정 값과 비교한다.

  • 펄스폭(Pulse width) 출력 모듈
    • 초음파 거리 센서(HC-SR04)
    • 초음파 거리 센서(HC-SR04) 모듈을 사용하는 구성도 예

    • 초음파 거리 센서(HC-SR04) 모듈을 제어하는 프로그램 예
      • 초음파 거리 센서(HC-SR04) 모듈은 초음파를 반사하는 물체까지의 거리에 비례한 Pulse 폭을 출력한다.

      • 초음파 거리 센서(HC-SR04) 모듈을 제어하는 프로그램 예(Atmel Studio 7): sensor_pulse_width_HC_SR04.zip
      • 아래 프로그램 예는 sensor_pulse_width_HC_SR04 프로그램 중 Timer를 이용하여 Trigger 신호를 출력하고 펄스폭을 측정하는 프로그램 예이다. 전체 프로그램은 sensor_pulse_width_HC_SR04.zip에 포함되어 있다.

        
        #include <avr/io.h>
        #include <avr/interrupt.h>
        #include <stdio.h>
        
        #include "gnu_sciutil.h"
        #include "timer1_input_capture_pulse_width_measure.h"
        
        #define TRIGER_PORT PORTE
        #define TRIGER_DDR DDRE
        #define TRIGER_BIT 3
        #define CAPTURE_PORT PORTE
        #define CAPTURE_DDR DDRE
        #define CAPTURE_PORT_BIT 7
        
        #define MEASURE_PERIOD 6250		// 0.1Sec <- 16uSec * 6250
        #define TRIGER_WIDTH 3			// 12uSSec <- 4uSec * 3
        
        volatile unsigned short int oldICR;
        unsigned short int pulseWidth, distance;
        unsigned long int long_dist;
        
        void init_port(void)
        {
        	TRIGER_DDR |= (1 << TRIGER_BIT);	// Triger 출력 Port를 Output port로 설정한다.
        	TRIGER_PORT &= ~(1 << TRIGER_BIT);	// Triger 출력 Port를 초기화(0)
        	CAPTURE_DDR &= ~(1 << CAPTURE_PORT_BIT);  // ICP1(PD4 : J2 --> 16번)을 
        	CAPTURE_PORT &= ~(1 << CAPTURE_PORT_BIT); // ? Input Capture Register Tregger 신호의 입력 단자로 사용 한다.
        }
        
        // Timer1A의 Output Compare Register(OCR1A)는 측정 주기 발생을 위하여 사용 한다.
        //   측정주기 0.1Sec <- 16uSec * 6250
        // Prescale clk/256, Clock 주기 : 16uSec
        void timer1_init(void)
        {
        	// (0 << WGM13) | (0 << WGM12)  | (0 << WGM11) | (0 << WGM10) : Normal mode
        	// (0 << COM1A1) | (0 << COM1A0) : Normal port operation, OCnA disconnected.
        	// (1 << CS12) | (0 << CS11) | (0 << CS10) : 100  prescale ck/256, Clock 주기 : 16uSec
        	TCCR1B = (1 << CS12) | (0 << CS11) | (0 << CS10);  
        
        	TCNT1 = 0x00;			//Timer/Counter1 Register 초기화
        	OCR1A = TCNT1 + MEASURE_PERIOD;	// 0.1Sec <- 16uSec * 6250
        	// OCIE1A : Output Compare A Match Interrupt Enable.
        	TIMSK = (1 << OCIE1A);
        }
        
        // Timer3A의 Output Compare Register(OCR3A)는 HC-SR04의 Trigger pulse를 발생을 위하여 사용 한다.
        // Timer3A의 Input Capture Register는 펄스 폭을 측정 하기 위한 Rg로 사용 한다.
        // 펄스 폭(High 상태) 측정 : Timer3, ICR3를 사용 한다.
        // Prescale clk/64, Clock 주기 : 4uSec
        void timer3_init(void)
        {
        	// (0 << WGM33) | (0 << WGM32)  | (0 << WGM31) | (0 << WGM30) : Normal mode
        	// (0 << COM3A1) | (0 << COM3A0) : Normal port operation, OCnA disconnected.
        	// TCCR3A = (0 << COM3A1) | (0 << COM3A0);
        	// ICNC3: Input Capture Noise Canceler.
        	// ICES3: Input Capture Edge Select. One : Rising edge, Zero : Falling edge.
        	// (1 << CS32) | (0 << CS31) | (0 << CS30) : 100  prescale ck/64, Clock 주기 : 4uSec
        	TCCR3B = (1 << ICNC3) | (1 << ICES3) | (0 << CS32) | (1 << CS31) | (1 << CS30);
        	TCNT3 = 0x00;			//Timer/Counter1 Register 초기화
        	// TICIE3 ; Input Capture Interrupt Disable.
        	// OCIE3A : Output Compare A Match Interrupt Disable.
        	ETIMSK &= ~(1 << OCIE3A);
        	ETIMSK &= ~(1 << TICIE3);
        }
        
        void init_timer(void){
        	init_port();
        	timer1_init();
        	timer3_init();
        }
        
        // 측정 주기를 발생
        SIGNAL(TIMER1_COMPA_vect)
        { 
        	// OCIE1A : Output Compare A Match Interrupt Disable.
        	TIMSK &= ~(1 << OCIE1A);
        	// 측정 주기
        	OCR1A = TCNT1 + MEASURE_PERIOD;	// 0.1Sec <- 16uSec * 6250
        
        	// Triger 신호를 출력한다. Triger Port <- High
        	TRIGER_PORT |= (1 << TRIGER_BIT);
        	// Triger 신호 폭 설정(12uSec <- 4uSec * 3)
        	OCR3A = TCNT3 + TRIGER_WIDTH;
        	// Trigger pulse clear를 위한 Timer3 interrupt enable 
        	ETIMSK |= (1 << OCIE3A);
        	// 측정 주기 발생 Timer interrupt enable.
        	TIMSK |= (1 << OCIE1A);
        }
        
        // HC-SR04의 Trigger pulse를 Low로 하고 Echo 신호의 펄스폭 측정을 Enable 한다.
        SIGNAL(TIMER3_COMPA_vect)
        {
        	// OCIE1B : Output Compare B Match Interrupt Disable.
        	ETIMSK &= ~(1 << OCIE3A);
        	// Triger Port <- Low
        	TRIGER_PORT &= ~(1 << TRIGER_BIT);
        
        	// Input Capture Interrupt가 Rising edge에서 발생 하도록 설정 한다.
        	// ICES3: Input Capture Edge Select. One : Rising edge, Zero : Falling edge.
        	TCCR3B |= (1 << ICES3);
        	// TICIE3: Input Capture Interrupt Enable.
        	ETIMSK |= (1 << TICIE3);
        }
        
        // HC-SR04의 Echo 펄스폭을 측정하고 결과를 출력한다. 
        SIGNAL(TIMER3_CAPT_vect)
        { 
        	ETIMSK &= ~(1 << TICIE3);  // TICIE3 ; Input Capture Interrupt Disable.
        	// 만약 현재 Rising edge에서 Input Capture Interrupt가 발생된 경우
        	if((TCCR3B & (1 << ICES3)) != 0){
        		// 펄스 폭 측정을 위하여 현재 Capture된 ICR3 값을 저장 한다.
        		oldICR = ICR3;
        		// 다음 Interrupt는 Falling edge에서 발생 하도록 설정 한다.
        		// ICES3: Input Capture Edge Select. One : Rising edge, Zero : Falling edge.
        		TCCR3B &= ~(1 << ICES3);  
        		// TICIE3 ; Input Capture Interrupt Enable.
        		ETIMSK |= (1 << TICIE3);
        	}
        	// 만약 현재 Falling edge에서 Input Capture Interrupt가 발생된 된 경우
        	else{
        		// 펄스 폭을 측정(계산) 한다.
        		pulseWidth = ICR3 - oldICR;
        		// pulseWidth를 uSec(*4)로 변환한 다음 거리(cm)로 변환한다.
        		// 거리 cm <- (pulseWidth * 4(uSec)) / 58 <- ((pulseWidth * 4(uSec)) * 17) / 1000
        		// 거리 cm <- (pulseWidth * 17) / 250
        		long_dist = ((unsigned long)pulseWidth * 17) / 250;
        		distance = long_dist;
        		// 측정 된 펄스 폭 값을 모니터에 출력 한다.
        		SCI_OutChar(CR);
        		SCI_OutString("    ");
        		SCI_OutChar(CR);
        		// 거리(cm)를 부호 없는 10진수로 출력한다.
        		SCI_OutUDec(distance);
        	}
        }
                  
      • 실험을 위한 회로 구성
        • PC와 AVR 사이에 UART 통신선을 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 RXD와 Atmega128의 TXD0(PE1)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 TXD와 Atmega128의 RXD0(PE0)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 GND와 Atmega128의 GND를 연결한다.
          • 주: 컴퓨터에 Serial Port가 없는 경우 "USB --> UART(RS232) 변환 모듈"를 사용(USB Port를 Serial Port로 변환)하여 연결한다. 개발보드에 따라서는 "USB --> UART(RS232) 변환 모듈"을 내장하고 있는 경우가 많기 때문에 본인이 사용하는 개발보드에서 확인 하여야 한다.

        • Atmega128 개발보드와 센서 모듈 연결
          • Atmega128 개발보드 Trigger output pin(PE3) <-> HC-SR04 센서의 Trig pin
          • Atmega128 개발보드 Pulse input pin(PE7) <-> HC-SR04 센서의 Echo pin
          • Atmega128 개발보드 VDD(5V} <-> HC-SR04 센서 Vdd pin
          • Atmega128 개발보드 GND <-> HC-SR04 센서 GND
      • 실험 방법
        • sensor_pulse_width_HC_SR04.zip 파일을 Download하여 압축을 해제한다.
        • sensor_pulse_width_HC_SR04.hex를 개발 보드에 Upload 한다.
        • 모니터 프로그램(예: OC-Console, Tera Term)을 설치(이미 설치되어 있는 경우에는 실행만 하면됨)하고 실행 한다.
        • 모니터 프로그램에서 필요한 설정(Serial Port와 Baudrate(19200) 등을 설정)을 한다.
        • 실험:
          • 개발 보드의 Reset SW를 누르면 모니터에 "Pulse width output sensor testing." 메세지가 출력된다.
          • 0.1Sec 주기로 거리가 측정되어 모니터에 출력된다.
          • 초음파 거리센서 앞에 반사체의 위치를 변경하며 실험한다.

  • Single-Wire 통신을 사용하는 센서
    • 온도/습도 센서(DHT11/DHT22) 모듈
    • DHT11/DHT22 센서 모듈을 사용하는 구성도 예

      DHT11 센서 모듈에 따른의 Pin 배치 예

      DHT11 센서

      DHT11 모듈 Pin 예 1번

      DHT11 모듈 Pin 예 2번

      주: 위 예와 같이 DHT11 센서 모듈의 제작 회사에 따라 Pin 배치와 Pull-up 저항의 내장여부가 다르기 때문에 주의를 요함. 2번 모듈인 경우에는 10K Pull-up 저항이 내장되어 있다.


    • 온도/습도 센서(DHT11/DHT22) 모듈을 제어하는 프로그램 예
      • 온도/습도 센서(DHT11/DHT22) 모듈은 측정한 온도/습도 값을 디지털 데이터로 변환하여 Single-Wire 통신 프로토콜로 출력한다.

        DHT11 센서의 Data signal timming diadram 예. 더 자세한 자료는 DHT11 Data sheet에서 확인하기 바람.

      • 온도/습도 센서(DHT11) 모듈을 제어하는 프로그램 예(Atmel Studio 7): single_wire_comm_DHT11.zip
      • 아래 프로그램 예는 single_wire_comm_DHT11 프로그램 중 측정 결과를 읽는 함수와 결과를 모니터에 출력하는 함수 예이다. 전체 프로그램은 single_wire_comm_DHT11.zip에 포함되어 있다.

        
        #include <avr/io.h>
        #include "timer0_clock.h"
        #include "gnu_sciutil.h"
        
        // DEFINE SETUP
        #define SENSOR_DDR				DDRC
        #define SENSOR_PORT				PORTC
        #define SENSOR_PIN				PINC
        #define SENSOR_PIN_BIT			PC5
        
        // GLOBAL VARIABLES
        uint8_t DHT11Data[5] = {0};
        static uint8_t DHT11Init = 0;
        
        // DHT11 초기화
        void DHT11Setup(){
        	// Power on 후 DHT11 이 안정화(사용 가능한 상태)되는데 약 1초 정도가 필요하다.
        	delay(2000);
        	// SENSOR_PORT를 output port로 설정한다.
        	SENSOR_DDR |= 1 << SENSOR_PIN_BIT;
        	// Set setup flag
        	DHT11Init = 1;
        }
        
        // 온도를 출력하는 함수
        void DHT11DisplayTemperature(){
        		    SCI_OutString("온도: ");
        		    SCI_OutUDec(DHT11Data[2]);
        		    SCI_OutChar('.');
        		    SCI_OutUDec(DHT11Data[3]);
        			SCI_OutChar('C');
        }
        
        // 습도를 출력하는 함수
        void DHT11DisplayHumidity(){
        		    SCI_OutString("습도: ");
        		    SCI_OutUDec(DHT11Data[0]);
        		    SCI_OutChar('.');
        		    SCI_OutUDec(DHT11Data[1]);
        		    SCI_OutChar('%');
        }
        
        // DHT11의 습도와 온도 값을 읽는 함수
        int8_t DHT11ReadData(){
        	uint8_t sensor_bytes, bits, buffer=0, timeout = 0, checksum;
        	
        	// 만약  DHT11 초기화가 실행되지 않은 경우  DHT11를 초기화한다.
        	if(DHT11Init == 0) DHT11Setup();
        	
        	// START signal을 Sensor에 Send 한다.
        	//   SENSOR_PORT를 output port로 설정한다.
        	SENSOR_DDR |= (1 << SENSOR_PIN_BIT);
        	SENSOR_PORT &= ~(1 << SENSOR_PIN_BIT);	// Pin에 LOW를 출력한다.
        	delay(20);	// Pin의 LOW 상태를 최소 18 mSec 동안 유지한다.
        
        	// SENSOR_DDR을 Input port(high Z)로 설정한다.
        	// Data line에 신호가 없는 경우 Pull-up 저항에 의하여 Data line은  HIGH 상태가된다.
        	SENSOR_DDR &= ~(1 << SENSOR_PIN_BIT);
        	delayMicroseconds(32); // Wait for 20-40 us
        	
        	// Sensor 응답 신호를 Check 한다. 80us LOW -> 80us HIGH signal
        	// 만약 신호가 HIGH 이면(Sensor didn't respond) Error code를 Return 한다.
        	if(SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        		return 0; // Error code
        	}
        	
        	// Sensor가 LOW signal을 Send 한 경우 HIGH 신호를 기다린다.
        	delayMicroseconds(82);
        	
        	// 만약 HIGH인 경우 Sensor는 Data를 Send 할 준비가 된 상태이다.
        	if(SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        		delayMicroseconds(82); // HIGH 신호가 종료되기를 기다린다.
        		// 만약 HIGH 상태가 계속되면 Error 가 발생한 것이기 때문에 0를 Return 한다.
        		if(SENSOR_PIN & (1 << SENSOR_PIN_BIT)) return 0;
        	}else{
        		return 0; // Error code
        	}
        	
        	// Sensor로 부터 Data(5 Byte)를 읽는다.
        	for(sensor_bytes = 0; sensor_bytes < 5; sensor_bytes++){
        		// Sensor로 부터 수신한 Bit Data를 Byte Data로 변환하는 값을 저장하는 buffer를 초기화한다.
        		buffer = 0;
        		// 8 Bits를 수신하여 Byte로 변환한다.	
        		for(bits = 0; bits < 8; bits++){
        			// 각 Bits를 수신할 때 마다 Signal이 Low인 상태를 50uSec 동안 기다린다.
        			while(~SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        				// Signal Low 상태를 기다리는 시간을 최대 80uSec로 제한한다.
        				// Signal이 Low인 상태를 80uSec 이상 지속하면 Error code(Return 0)를 Return한다.
        				timeout++;
        				if(timeout > 8) break;
        				delayMicroseconds(10);
        			}
        			timeout = 0;
        			
        			// Signal이 HIGH 이면 Bit 값을 읽는다.
        			if(SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        				// bit 값이 0인 경우 26-28 us 동안 HIGH 이고, bit 값이 1인 경우 70uSec 동안 High이다.
        				delayMicroseconds(40);
        				// 만약 Signal이 HIGH 상태이면 buffer의 bits 위치를 1로 Set 한다.
        				if(SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        					// buffer의 bits 위치를 1로 Set 한다.
        					// Sensor는 MSB 먼저 송신한다.
        					buffer |= 1 << (7 - bits);
        				}
        				
        				// Signal의 HIGH 상태가 종료되기를 80uSec 동안 기다린다.
        				while(SENSOR_PIN & (1 << SENSOR_PIN_BIT)){
        					// Signal High 상태를 기다리는 시간을 최대 80uSec로 제한한다.
        					timeout++;
        					if(timeout > 8) break;
        					delayMicroseconds(10);
        				}
        				timeout = 0;
        			}else{
        				// Signal LOW 인 상태를 지속하면 0를 Return(Error response) 한다.
        				return 0;
        			}
        		}
        		
        		// 수신한 Byte data를 DHT11Data[sensor_bytes]에 저장한다.
        		DHT11Data[sensor_bytes] = buffer;
        	}
        	
        	// Data transmision 이 조료되기를 기다린다. Sensor는 50uSec 동안 LOW를 출력하고,
        	// 다음 START command 이 있을 때까지 Low-power consumption mode가 된다.
        	// Sensor 가 안정되어 다음 측정이 가능한 상태까지 약 1Sec 정도의 시간이 필요하다.
        	delayMicroseconds(60);
        	
        	// Idle 상태일 때 Sensor DATA line은 HIGH 상태를 유지하여야 한다.
        	// 아래와 같이 AVR에서 High를 출력 할 수도 있지만 더 좋은 방법은 
        	// Idle 상태(high Z)에서는 Pull-up 저항으로 High 상태가 되도록 하는 방법이다.
        	// Pull-up 저항으로 High 상태가 되도록 하기 위하여는 적당한 값의 Pull-up 저항을 사용하여야 한다.
        	// AVR에서 High를 출력하는 코드 예
        	//   SENSOR_DDR |= 1 << SENSOR_PIN_BIT;
        	//   SENSOR_PORT |= 1 << SENSOR_PIN_BIT;
        	
        	// Data transmission errors를 check 한다.
        	checksum = DHT11Data[0] + DHT11Data[1] + DHT11Data[2] + DHT11Data[3];
        	if(checksum != DHT11Data[4]){
        		return -1; // Checksum error code
        	}	
        	// OK return code
        	return 1;
        }
                  

      • 실험을 위한 회로 구성
        • PC와 AVR 사이에 UART 통신선을 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 RXD와 Atmega128의 TXD0(PE1)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 TXD와 Atmega128의 RXD0(PE0)를 연결한다.
          • 컴퓨터(USB --> UART(RS232) 변환 모듈)의 GND와 Atmega128의 GND를 연결한다.
          • 주: 컴퓨터에 Serial Port가 없는 경우 "USB --> UART(RS232) 변환 모듈"를 사용(USB Port를 Serial Port로 변환)하여 연결한다. 개발보드에 따라서는 "USB --> UART(RS232) 변환 모듈"을 내장하고 있는 경우가 많기 때문에 본인이 사용하는 개발보드에서 확인 하여야 한다.

        • Atmega128 개발보드와 센서 모듈 연결
          • Atmega128 개발보드 Data pin(PC5) <-> DHT11 센서의 Data pin
          • Atmega128 개발보드 VDD(5V} <-> DHT11 센서 Vcc pin
          • Atmega128 개발보드 GND <-> DHT11 센서 GND
      • 실험 방법
        • single_wire_comm_DHT11.zip 파일을 Download하여 압축을 해제한다.
        • single_wire_comm_DHT11.hex를 개발 보드에 Upload 한다.
        • 모니터 프로그램(예: OC-Console, Tera Term)을 설치(이미 설치되어 있는 경우에는 실행만 하면됨)하고 실행 한다.
        • 모니터 프로그램에서 필요한 설정(Serial Port와 Baudrate(19200) 등을 설정)을 한다.
        • 실험:
          • 개발 보드의 Reset SW를 누르면 모니터에 "DHT11 Testing" 메세지가 출력된다.
          • 약 2초 주기로 DHT11 센서의 측정 결과(온도와 습도)가 Console에 출력된다.
          • 센서의 온도와 습도를 변경하며 결과를 관측한다.

  • I2C 통신을 사용하는 모듈