마이크로 컨트롤러 슬립 레이스 조건


11

다음 코드를 실행하는 마이크로 컨트롤러가 제공됩니다.

volatile bool has_flag = false;

void interrupt(void) //called when an interrupt is received
{
    clear_interrupt_flag(); //clear interrupt flag
    has_flag = true; //signal that we have an interrupt to process
}

int main()
{
    while(1)
    {
        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

if(has_flag)조건이 거짓으로 평가되고 수면 명령을 실행하려고 한다고 가정합니다 . 오른쪽 우리가 잠 명령을 실행하기 전에, 우리는 인터럽트를받을 수 있습니다. 인터럽트를 떠난 후 휴면 명령을 실행합니다.

이 실행 순서는 다음과 같은 이유로 바람직하지 않습니다.

  • 마이크로 컨트롤러가 깨어나서 호출하는 대신 절전 모드로 전환되었습니다 process().
  • 이후에 인터럽트가 수신되지 않으면 마이크로 컨트롤러가 깨어날 수 없습니다.
  • process()다음 인터럽트까지 호출 이 연기됩니다.

이 경쟁 조건이 발생하지 않도록 코드를 작성하는 방법은 무엇입니까?

편집하다

ATMega와 같은 일부 마이크로 컨트롤러는 이러한 상태가 발생하지 않도록하는 슬립 활성화 비트를 가지고 있습니다 (Kvegaoro가이를 지적 해 주셔서 감사합니다). JRoberts는이 동작을 보여주는 구현 예제를 제공합니다.

PIC18과 같은 다른 마이크로에는이 비트가 없으며 여전히 문제가 발생합니다. 그러나 이러한 마이크로는 전역 인터럽트 활성화 비트가 설정되어 있는지 여부에 관계없이 인터럽트가 코어를 깨울 수 있도록 설계되었습니다 (이 점을 지적 해 주셔서 감사합니다). 이러한 아키텍처의 경우 해결책은 휴면 직전에 글로벌 인터럽트를 비활성화하는 것입니다. 슬립 명령을 실행하기 직전에 인터럽트가 발생하면 인터럽트 핸들러가 실행되지 않고 코어가 깨어나고 글로벌 인터럽트가 다시 활성화되면 인터럽트 핸들러가 실행됩니다. 의사 코드에서 구현은 다음과 같습니다.

int main()
{
    while(1)
    {
        //clear global interrupt enable bit.
        //if the flag tested below is not set, then we enter
        //sleep with the global interrupt bit cleared, which is
        //the intended behavior.
        disable_global_interrupts();

        if(has_flag) //if we had an interrupt
        {
            has_flag = false; //clear the interrupt flag
            enable_global_interrupts();  //set global interrupt enable bit.

            process(); //process the interrupt
        }
        else
            sleep(); //place the micro to sleep
    }
}

이것이 실용적이거나 이론적 인 질문입니까?
AndrejaKo

이론적으로 당신은 매번 (허용 가능한 값을 입력) ms마다 당신을 깨우고 아무것도 할 필요가 없다면 잠자기 상태로 돌아가는 타이머를 사용합니다.
Grady Player

1
내가 할 것 interrupt_flagint로서 int, 그리고 그것을 인터럽트가 때마다 증가. 그런 다음 변경 if(has_flag)while (interrupts_count)한 후 잠을. 그럼에도 불구하고 while 루프를 종료 한 후에 인터럽트가 발생할 수 있습니다. 이것이 문제라면 인터럽트 자체에서 처리합니까?
angelatlarge

1
ATmega328 인 경우 인터럽트에서 휴면 모드를 비활성화 할 수 있습니다. 따라서 설명하는 경쟁 조건이 발생하면 휴면 기능이 무시되고 다시 루프되어 처리됩니다. 짧은 대기 시간으로 인터럽트. 그러나 타이머를 사용하여 최대 대기 시간 이하의 간격으로 깨우는 것도 훌륭한 솔루션입니다.
Kvegaoro

1
@TRISAbits : PIC 18x에서 필자의 답변에서 설명한 접근 방식은 제대로 작동합니다 (해당 부분을 사용할 때의 일반적인 디자인입니다).
supercat

답변:


9

이 경우 일반적으로 어떤 종류의 하드웨어 지원이 있습니다. 예를 들어, sei인터럽트를 활성화하기 위한 AVR의 명령어는 다음 명령어가 완료 될 때까지 활성화를 연기합니다. 그것으로 할 수 있습니다 :

forever,
   interrupts off;
   if has_flag,
      interrupts on;
      process interrupt;
   else,
      interrupts-on-and-sleep;    # won't be interrupted
   end
end

이 예에서 빠졌을 인터럽트는이 경우 프로세서가 슬립 시퀀스를 완료 할 때까지 보류됩니다.


좋은 대답입니다! 제공하는 알고리즘은 실제로 AVR에서 실제로 잘 작동합니다. 제안 해 주셔서 감사합니다.
TRISAbits

3

많은 마이크로 컨트롤러에는 특정 인터럽트 원인 (일반적으로 인터럽트 컨트롤러 모듈 내)을 활성화 또는 비활성화 할 수있을뿐만 아니라 CPU 코어 내에 인터럽트 요청의 수락 여부를 결정하는 마스터 플래그가 있습니다. 인터럽트 요청이 코어에 도달하면 코어가 실제로이를 수용 할 수 있는지 여부에 관계없이 많은 마이크로 컨트롤러가 슬립 모드를 종료합니다.

이러한 설계에서 안정적인 절전 동작을 달성하는 간단한 방법은 메인 루프에서 플래그를 지우고 프로세서가 깨어나는 이유를 알고 있는지 확인하는 것입니다. 해당 시간 동안 발생하여 인터럽트에 영향을 줄 수있는 모든 인터럽트는 플래그를 설정해야합니다. 메인 루프가 깨어있는 원인을 찾지 못하고 플래그가 설정되지 않은 경우 메인 루프는 인터럽트를 비활성화하고 플래그를 다시 확인해야합니다 (아마도 몇 개의 NOP 명령 후에 보류중인 인터럽트가 발생할 수있는 경우) 다음 명령과 관련된 피연산자 페치가 이미 수행 된 후 비활성화 인터럽트 명령이 처리 될 수 있습니다]. 플래그가 여전히 설정되어 있지 않으면 절전 모드로 전환하십시오.

이 시나리오에서 메인 루프가 인터럽트를 비활성화하기 전에 발생하는 인터럽트는 최종 테스트 전에 플래그를 설정합니다. 휴면 명령 전에 서비스하기에 너무 늦게 보류되는 인터럽트는 프로세서가 휴면 상태가되지 못하게합니다. 두 상황 모두 괜찮습니다.

수면 종료는 사용하기에 좋은 모델이지만 모든 응용 프로그램이 실제로 "적합한"것은 아닙니다. 예를 들어, 에너지 효율적인 LCD를 사용하는 장치는 다음과 같은 코드로 가장 쉽게 프로그래밍 할 수 있습니다.

void select_view_user(int default_user)
{
  int current_user;
  int ch;
  current_user = default_user;
  do
  {
    lcd_printf(0, "User %d");
    lcd_printf(1, ...whatever... );
    get_key();
    if (key_hit(KEY_UP)) {current_user = (current_user + 1) % MAX_USERS};
    if (key_hit(KEY_DOWN)) {current_user = (current_user + MAX_USERS-1) % MAX_USERS};
    if (key_hit(KEY_ENTER)) view_user(current_user);
  } while(!key_hit(KEY_EXIT | KEY_TIMEOUT));
}

아무 버튼도 누르지 않고 다른 것도 진행되지 않으면 get_key메소드 실행 중에 시스템이 절전 모드로 전환되지 않아야 할 이유가 없습니다 . 키가 인터럽트를 트리거하고 상태 머신을 통해 모든 사용자 인터페이스 상호 작용을 관리하는 것이 가능할 수 있지만, 위와 같은 코드는 종종 소형 마이크로 컨트롤러의 전형적인 높은 모드 사용자 인터페이스 흐름을 처리하는 가장 논리적 인 방법입니다.


큰 답변을 주셔서 감사합니다. 인터럽트를 비활성화 한 다음 절전 모드로 전환하는 것은 전역 인터럽트 비트가 설정 / 지워 졌는지 여부에 관계없이 코어가 모든 인터럽트 소스에서 깨어 나면 환상적인 솔루션입니다. PIC18 인터럽트 하드웨어 회로도를 살펴보면이 솔루션이 효과가 있습니다.
TRISAbits

1

인터럽트시 깨어 지도록 마이크로를 프로그래밍하십시오.

구체적인 세부 사항은 사용중인 마이크로에 따라 다릅니다.

그런 다음 main () 루틴을 수정하십시오.

int main()
{
    while(1)
    {
        sleep();
        process(); //process the interrupt
    }
}

1
문제에서 Wake-on-Interrupt 아키텍처가 가정됩니다. 나는 당신의 대답이 질문 / 문제를 해결한다고 생각하지 않습니다.
angelatlarge

@angelatlarge 포인트가 승인되었습니다. 도움이 될만한 코드 예제를 추가했습니다.
jwygralak67

@ jwygralak67 : 제안 해 주셔서 감사하지만 제공하는 코드는 문제를 process () 루틴으로 옮기기 만하면 process () 본문을 실행하기 전에 인터럽트가 발생했는지 테스트해야합니다.
TRISAbits

2
인터럽트가 발생하지 않았다면 왜 깨어 있습니까?
JRobert

1
@JRobert : 이전 인터럽트에서 깨어나서 process () 루틴을 완료하고 if (has_flag) 테스트를 마치고 잠자기 직전에 또 다른 인터럽트를 받으면 다른 인터럽트가 발생합니다. 질문.
TRISAbits
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.