프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

Blink예제를 펌웨어 레벨 관점에서 분석

2016-02-16 14:20:19

보통 MCU(Micro Control Unit)의 FW(Firmware) 개발하는 사람들은 avr-gcc,  IAR, Code vision등의 컴파일러를 사용하여 개발한다.

Arduino IDE의 경우, AVR-GCC를 이용하여 컴파일 한다. 

AVR-GCC는 대표적인 오픈 소스 기반의 컴파일러로 왠만한 상용컴파일러에 뒤지지 않으며, ATmel Stduio와도 통합되어 편리하게 사용할수 있다.

곧 Arduino IDE에서 GCC기반의 코딩을 하면 먹힌다.

여기서는 Arduino IDE의 Blink예제를 펌웨어 레벨 관점에서 분석하여 보자...

Blink예제는 오렌지 보드의 13이라고 써져있는 LED를 약 1초 간격으로, 점등과 소등을 반복하는 예제이다.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.

  Most Arduinos have an on-board LED you can control. On the Uno and
  Leonardo, it is attached to digital pin 13. If you're unsure what
  pin the on-board LED is connected to on your Arduino model, check
  the documentation at http://www.arduino.cc

  This example code is in the public domain.

  modified 8 May 2014
  by Scott Fitzgerald
 */


// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

 

아두이노 설치 폴더의 해당 경로에서 wiring_digital.c를 보면, digitalWrite()함수의 내부를 확인할 수 있다.

(설치경로 예> D:\Util\arduino\arduino-1.6.6-windows\arduino-1.6.6\hardware\arduino\avr\cores\arduino\wiring_digital.c)

void digitalWrite(uint8_t pin, uint8_t val)
{
    uint8_t timer = digitalPinToTimer(pin); ...................ⓐ
    uint8_t bit = digitalPinToBitMask(pin); ...................ⓑ
    uint8_t port = digitalPinToPort(pin);   ...................ⓒ 
    volatile uint8_t *out; ....................................ⓓ 

    if (port == NOT_A_PIN) return;

    // If the pin that support PWM output, we need to turn it off
    // before doing a digital write.
    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    out = portOutputRegister(port); ...........................ⓔ 

    uint8_t oldSREG = SREG;         ...........................ⓕ  
    cli();

    if (val == LOW) {
        *out &= ~bit;    ......................................ⓖ 
    } else {
        *out |= bit;     ......................................ⓗ 
    }

    SREG = oldSREG;      ......................................ⓘ 
}

ⓐ uint8_t timer = digitalPinToTimer(pin); 

uint8_t는 unsigned char나 signed char로 변수 선언에 있어서 데이터의 크기가 프랫폼마다 다르기 때문에, 사용되는 고정 길이 데이터 형식이다. Primitive System Data Type이라고 부른다. 이 코드에서 uint8_t대신 unsigned char로 사용하여도 상관없다.

 stdint.h의 헤더파일에 명시되어 있다.

 

digitalPinToTimer는  #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) ) 로

Arduino.h에 선언되어 있다. 

(설치경로 예>D:\Util\arduino\arduino-1.6.6-windows\arduino-1.6.6\hardware\arduino\avr\cores\arduino\Arduino.h)

 

pgm_read_byte라는 것은 오렌지 보드의 MCU인 ATmega328P의 프로그램 메모리(32KB)에 읽는 것이다.

MCU는 프로그램 메모리(32KB), SRAM(2KB)이다. 불과 2KB밖에 안되는 데이터 메모리(SRMA)의 사용을 줄이기 위하여, 고안된 것이다.

digital_pin_to_timer_PGM은 Pins_arduino.h에 명시 되어 있다.

const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
    NOT_ON_TIMER, /* 0 - port D */
    NOT_ON_TIMER,
    NOT_ON_TIMER,
    // on the ATmega168, digital pin 3 has hardware pwm
#if defined(__AVR_ATmega8__)
    NOT_ON_TIMER,
#else
    TIMER2B,
#endif
    NOT_ON_TIMER,
    // on the ATmega168, digital pins 5 and 6 have hardware pwm
#if defined(__AVR_ATmega8__)
    NOT_ON_TIMER,
    NOT_ON_TIMER,
#else
    TIMER0B,
    TIMER0A,
#endif
    NOT_ON_TIMER,
    NOT_ON_TIMER, /* 8 - port B */
    TIMER1A,
    TIMER1B,
#if defined(__AVR_ATmega8__)
    TIMER2,
#else
    TIMER2A,
#endif
    NOT_ON_TIMER,
    NOT_ON_TIMER,
    NOT_ON_TIMER,
    NOT_ON_TIMER, /* 14 - port C */
    NOT_ON_TIMER,
    NOT_ON_TIMER,
    NOT_ON_TIMER,
    NOT_ON_TIMER,
};

#endif

const는 읽기 전용으로 사용한다는 것이며, 처음 상수를 정의 할때, 사용된다. 

PROGMEM은 데이터를 프로그램 메모리(32KB)에 저장하는 메크로이다. 왜 사용하는지는 위에서 설명하였다.

digitalWrite(13, HIGH)을 하였을 경우, digital_pin_to_timer_PGM에서 13번째에 해당하는 NOT_ON_TIMER 로

결국, timer = digitalPinToTimer(13); 의 리턴값은 NOT_ON_TIMER 이다. 

만약, 오렌지 보드의 PWM포트인 digital IO3을 사용했을때는, timer = digitalPinToTimer(3);  

으로 리턴값이 TIMER2B로 설정되는대, 

digitalWrite의 함수의 if (timer != NOT_ON_TIMER) turnOffPWM(timer); 

NOT_ON_TIMER가 아니면 PWM기능을 끄라는 명령을 한다. 즉. PWM기능을 이미 사용하고 있을때, 기능을 꺼두기 위함이다.

Requiem

blink예제