[ATmega128] volatile 변수

마이크로컨트롤러/ATmega128 · 2020. 9. 2. 19:17

volatile

  • 기본적으로 C/C++ 컴파일러는 최적화의 기능을 가지고 있다. 비효율적이거나 불필요한 연산은 생략하고 코드를 최적화시킨다.
  • 변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 만든다.

사용방법

기본적인 변수 사용

int a
static int a

volatile 변수 사용

volatile int a
static volatile int a

사용하는 이유

1. 컴파일러에서 최적화를 방지한다.

2. 후속 프로그램이 변수를 다룰 때 레지스터가 아닌 메모리에서 읽어오도록 한다.

3. 만약 16비트 마이크로 컨트롤러에서 32비트(int, long) 변수를 읽어오려면 두 번에 걸쳐 메모리를 읽어야 하는데, 그 변수를 읽는 동안에 갑자기 값이 바뀔 수 있으므로 읽히는 동안 비트를 건드리지 못하게 한다.

 

C 소스코드

int main()
{
    int i = 0;
    
    while (i < 10)
    {
    	i++;
    }
    
    printf("%d\n", i);
}

위와 같이 작성된 코드는 컴파일러의 최적화로 인해 어셈블리어로 아래와 같은 구조로 진행한다.

int main()
{
    int i = 10; //while문을 제거하고 10을 할당
    
    printf("%d\n", i);
}

 

예시 1

int main()
{
  volatile unsigned int iCount = 0;

  *((volatile unsigned int *)0xFFFFF410) = 1;    // output activate  PIO_OER
  *((volatile unsigned int *)0xFFFFF400) = 1;    // pin activate    PIO_PER

  while (1)
  {
    for(iCount=0;1000000 >= iCount; ++iCount);

    *((volatile unsigned int *)0xFFFFF434) = 1;    // Enable 0V output  PIO_CODR

    for(iCount=0;1000000 >= iCount; ++iCount);
    *((volatile unsigned int *)0xFFFFF430) = 1;    // Enable 3.3V output  PIO_SODR
  }

  return 0;
}

위의 코드에서 iCount에 volatile 키워드를 없애버린다면,

for(iCount=0;1000000 >= iCount; ++iCount);

시간을 끌기 위해 사용된 위의 무한루프를 컴파일러는 레지스터로 초기값을 읽어와 연산 후 결과를 바로 iCoutn 변수에 대입해버린다.

(iCount = 1000000)

그러므로 사용자가 의도한 만큼의 딜레이를 줄 수 없게 된다.

 

예시 2

*((volatile unsigned int *)0xFFFFF410) = 1;

위의 0xFFFFF410에 접근할 때 캐쉬메모리같은 중간 단계에 값을 대입해놓지말고 직접접근만을 허용하라는 뜻이다.

 

위의 주소값에 volatile을 붙여주지 않는다면, 외부의 센서같은 I/O에서 값이 변화되어도 이미 레지스터로 들어온 값만 체크하고 실제 메모리에 저장된 값을 계속 읽어오지 않으므로 변화를 감지하지 못한다.

 

결론: volatile 키워드는 값을 메모리에서 cpu 레지스터로 불러왔다 연산 후 다시 메모리에 그 결괏값을 저장하는 과정을 컴파일러나 시스템에서 생략(메모리 접근 최소화)하지 못하도록 최적화에서 제외시킨다.