임베디드 시스템에서는 주기적으로 하드웨어를 감시해 상태가 변한 것을 감시하는 처리가 빈번히 실행된다. 이 처리를 폴링(polling)이라고 한다. 인터럽트 기능을 갖고 있지 않은 주변장치 등을 감시하기 위해 사용된다.
간단하게 말해서 폴링은 "프로그램이나 장치에서 다른 프로그램이나 장치들이 어떤 상태에 있는지를 지속적으로 검사하는 전송 제어 방식"이다.
이 때 주변장치의 감시를 위해 레지스터 주소를 지정한 처리를 작성했다고 하면, 컴파일러는 주변장치의 레지스터 주소를 알지 못하기 때문에 최적화 옵션을 지정하여 컴파일하면 의도하지 않은 상황으로 전개될 가능성이 있다.
이 때 volatile 선언으로 최적화를 하지 않도록 지정한다.
for(;;){
if((*(unsigned long*)(0x0000000F)) & 0x04){
// 수신이 완료되면
수신 처리
break;
}
}
위의 코드는 for 루프 안의 if 문에서 레지스터 주소를 체크하여 수신했는지를 판정한다.
하지만 위의 코드가 컴파일러에서 최적화되면
if((*(unsigned long*)(0x0000000F)) & 0x04){
// 수신 완료되면
for(;;){
//수신 처리
break;
}
}
if 문이 for 루프 밖으로 나가 한 번밖에 실행되지 않는다.
따라서 volatile 선언을 이용하여 최적화를 억제하여야 한다.
for(;;){
if((*(volatile unsigned long*)(0x0000000F)) & 0x04){
// 수신완료되면
수신 처리
break;
}
}
또 다른 예시를 살펴보면
static int foo;
void bar(void)
{
foo = 0;
while (foo != 255);
}
위의 코드에서 foo 값이 초기값 0 이후 while 루프 안에서 foo 값이 변하지 않아 while의 조건은 항상 true이다.
따라서 컴파일러는 다음과 같이 최적화한다.
void bar_optimized(void)
{
foo = 0;
while (true);
}
이렇게 되면 while의 무한 루프에 빠지게 되고 volatile을 사용하여 최적화를 방지할 수 있다.
static volatile int foo;
void bar (void)
{
foo = 0;
while (foo != 255);
}
이렇게 되면 개발자가 의도한 대로, 작성한 대로 기계어 코드가 생성된다. 위 프로그램은 무한루프라고 생각할 수 있지만 foo가 하드웨어 장치의 레지스터라면 하드웨어에 의해 값이 변할 수 있다.
따라서 volatile 선언은 하드웨어 값을 폴링할 때 사용할 수 있다.
'About > Embedded System' 카테고리의 다른 글
[Embedded] 인터럽트 핸들러(interrupt handler, AVR 예시) (1) | 2021.12.21 |
---|