마이크로 컨트롤러 및 FSM 예제의 인터럽트 처리


9

초기 질문

마이크로 컨트롤러의 인터럽트 처리에 대한 일반적인 질문이 있습니다. MSP430을 사용하고 있지만이 질문이 다른 uC로 확대 될 수 있다고 생각합니다. 코드를 따라 인터럽트를 자주 활성화 / 비활성화하는 것이 좋은 습관인지 아닌지 알고 싶습니다. 즉, 인터럽트에 민감하지 않은 코드 부분이 있거나 더 나쁜 경우 인터럽트를 수신해서는 안되는 코드가 있다면 다음과 같이하는 것이 좋습니다.

  • 인터럽트를 전에 비활성화 한 다음 중요 섹션 후에 인터럽트를 다시 활성화하십시오.
  • 각 ISR 내부에 플래그를 넣고 (인터럽트를 비활성화하는 대신) 중요 섹션 전에 플래그를 false로 설정 한 후 바로 true로 재설정하십시오. ISR 코드가 실행되지 않도록합니다.
  • 둘 중 어느 것도 아니므로 제안을 환영합니다!

업데이트 : 인터럽트 및 상태 차트

구체적인 상황을 알려 드리겠습니다. 4 개의 블록으로 구성된 상태 차트를 구현한다고 가정합니다.

  1. 전환 / 효과.
  2. 출구 조건.
  3. 참가 활동.
  4. 활동을하십시오.

이것은 교수가 대학에서 우리에게 가르친 것입니다. 아마도이 작업을 수행하는 가장 좋은 방법은 아닙니다.이 계획을 따르는 것입니다.

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

예를 STATE_A들어 버튼 세트 (debouce 시스템 등)에서 오는 인터럽트에 민감하고 싶다고 가정 해 봅시다 . 누군가이 버튼 중 하나를 누르면 인터럽트가 생성되고 입력 포트와 관련된 플래그가 변수에 복사됩니다 buttonPressed. 디 바운스가 어떤 식 으로든 (워치 독 타이머, 타이머, 카운터 등) 200ms로 설정되면 200ms buttonPressed이전에 새로운 값으로 업데이트 할 수 없습니다. 이것이 내가 당신에게 요구하는 것입니다 (물론 :)

STATE_A떠나기 전에 DO 활동에서 인터럽트를 활성화 하고 비활성화해야합니까?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

다음 번에 다음 반복에서 블록 1 (전환 / 효과)을 실행할 때 전환을 따라 확인 된 조건 buttonPressed이 I 의 이전 값을 덮어 쓴 후속 인터럽트에서 오는 것이 아니라고 확신합니다. 필요합니다 (250ms가 경과해야하기 때문에 이것이 불가능합니다).


3
상황에 대해 더 많이 알지 못하면 추천을하기가 어렵습니다. 그러나 때로는 임베디드 시스템에서 인터럽트를 비활성화해야합니다. 인터럽트를 놓치지 않도록 인터럽트를 짧은 시간 동안 만 비활성화하는 것이 좋습니다. 모든 인터럽트가 아닌 특정 인터럽트 만 비활성화 할 수 있습니다. 나는 당신이 묘사 한 것처럼 IRS 기술을 사용하는 것을 기억하지 못하므로 이것이 최선의 해결책인지 의심 스럽습니다.
kkrambo

답변:


17

첫 번째 전술은 언제라도 인터럽트가 발생해도 괜찮도록 전체 펌웨어를 설계하는 것입니다. 포 그라운드 코드가 원자 시퀀스를 실행할 수 있도록 인터럽트를 꺼야하는 것은 드물게 수행되어야합니다. 그 주위에는 종종 건축적인 방법이 있습니다.

그러나 기계는 당신에게 서비스를 제공하기 위해 존재합니다. 일반적인 경험 법칙은 나쁜 프로그래머가 실제로 나쁜 코드를 작성하지 못하게하는 것입니다. 그것은 훨씬 더 이해하고 기계가 작동하고 건축가 좋은 방법은 원하는 작업을 수행하기 위해 이러한 기능을 활용하는 방법을 정확하게.

사이클이나 메모리 위치가 빡빡하지 않으면 (확실히 발생할 수 있음) 그렇지 않으면 코드의 명확성과 유지 관리 성을 위해 최적화해야합니다. 예를 들어 클럭 틱 인터럽트에서 32 비트 카운터를 업데이트하는 16 비트 시스템이있는 경우 포 그라운드 코드가 카운터를 읽을 때 두 절반이 일치하는지 확인해야합니다. 한 가지 방법은 인터럽트를 끄고 두 단어를 읽은 다음 인터럽트를 다시 켜는 것입니다. 인터럽트 대기 시간이 중요하지 않으면 완벽하게 수용 할 수 있습니다.

인터럽트 지연이 적어야하는 경우, 예를 들어 하이 워드를 읽고, 로우 워드를 읽고, 하이 워드를 다시 읽고, 변경된 경우 반복 할 수 있습니다. 이렇게하면 포 그라운드 코드가 약간 느려지지만 인터럽트 대기 시간이 전혀 추가되지 않습니다. 다양한 작은 트릭이 있습니다. 또 다른 방법은 카운터를 증가시켜야 함을 나타내는 인터럽트 루틴에서 플래그를 설정 한 다음 포 그라운드 코드의 기본 이벤트 루프에서 플래그를 설정하는 것입니다. 카운터 인터럽트 속도가 충분히 느려서 플래그가 다시 설정되기 전에 이벤트 루프가 증분을 수행 할 경우 제대로 작동합니다.

또는 플래그 대신 한 단어 카운터를 사용하십시오. 포 그라운드 코드는 시스템을 마지막으로 업데이트 한 카운터를 포함하는 별도의 변수를 유지합니다. 라이브 카운터에서 부호없는 빼기에서 저장된 값을 빼서 한 번에 처리해야하는 틱 수를 결정합니다. 이를 통해 포 그라운드 코드 는 한 번에 최대 2 개의 N -1 이벤트 를 놓칠 수 있습니다. 여기서 N은 ALU가 원자 적으로 처리 할 수있는 기본 단어의 비트 수입니다.

각 방법에는 고유 한 장단점이 있습니다. 정답은 하나도 없습니다. 다시 한 번, 기계 작동 방식을 이해 하면 규칙이 필요하지 않습니다.


7

중요한 섹션이 필요한 경우 중요한 섹션을 보호하는 작업이 원자 적이며 중단 될 수 없는지 확인해야합니다.

따라서 단일 프로세서 명령어 (및 컴파일러 내장 함수를 사용하여 호출)에서 가장 일반적으로 처리되는 인터럽트 비활성화는 가장 안전한 방법 중 하나입니다.

시스템에 따라 인터럽트가 누락 될 수있는 것과 같은 문제가있을 수 있습니다. 일부 마이크로 컨트롤러는 전역 인터럽트 활성화 상태에 관계없이 플래그를 설정하며, 임계 섹션을 벗어나면 인터럽트가 실행되어 지연됩니다. 그러나 높은 속도로 인터럽트가 발생하면 인터럽트를 너무 오랫동안 차단하면 두 번째 인터럽트가 발생할 수 있습니다.

중요 섹션에서 하나의 인터럽트 만 실행하지 않아도되고 다른 인터럽트는 실행해야하는 경우 다른 접근 방식이 실행 가능한 것으로 보입니다.

인터럽트 서비스 루틴을 가능한 한 짧게 프로그래밍하고 있습니다. 그래서 그들은 단지 일반 프로그램 루틴 동안 점검되는 플래그를 설정했습니다. 그러나 그렇게하면 플래그가 설정되기를 기다리는 동안 경쟁 조건에주의하십시오.

많은 옵션이 있으며 반드시 이것에 대한 하나의 정답은 아닙니다. 이것은 신중한 디자인이 필요하고 다른 것보다 조금 더 생각할 가치가있는 주제입니다.


5

비정상적인 상황을 제외하고 코드 섹션이 중단없이 실행되어야한다고 결정한 경우 작업을 완료하는 데 가능한 최소 시간 동안 인터럽트를 비활성화 한 후 다시 활성화해야합니다.

각 ISR 내부에 플래그를 넣고 (인터럽트를 비활성화하는 대신) 중요 섹션 전에 플래그를 false로 설정 한 후 바로 true로 재설정하십시오. ISR 코드가 실행되지 않도록합니다.

이것은 여전히 ​​인터럽트 발생, 코드 점프, 점검, 리턴을 허용합니다. 코드가 이러한 많은 중단을 처리 할 수있는 경우 검사를 수행하지 않고 플래그를 설정하도록 ISR을 간단히 설계해야합니다 (단축적임). 일반적인 코드 루틴에서 플래그를 처리해야합니다. 누군가 인터럽트에 너무 많은 코드를 넣은 것처럼 들리며, 인터럽트를 사용하여 일반 코드에서 수행해야하는 더 긴 작업을 수행하는 것 같습니다.

인터럽트가 긴 코드를 처리하는 경우 제안 된 플래그로 문제를 해결할 수 있지만 인터럽트에서 과도한 코드를 제거하기 위해 코드를 리팩터링 할 수없는 경우 인터럽트를 비활성화하는 것이 좋습니다. .

플래그 방식으로 수행하는 주된 문제는 인터럽트를 전혀 실행하지 않는다는 것입니다. 나중에 영향을 미칠 수 있습니다. 대부분의 마이크로 컨트롤러는 인터럽트가 전체적으로 비활성화 된 경우에도 인터럽트 플래그를 추적 한 다음 인터럽트를 다시 활성화 할 때 인터럽트를 실행합니다.

  • 중요 섹션 동안 인터럽트가 발생하지 않으면 이후에 실행되지 않습니다.
  • 임계 섹션 동안 하나의 인터럽트가 발생하면 이후에 하나의 인터럽트가 실행됩니다.
  • 중요 섹션 동안 여러 인터럽트가 발생하면 이후에 한 번만 실행됩니다.

시스템이 복잡하고 인터럽트를보다 완벽하게 추적해야하는 경우 인터럽트를 추적하고 그에 따라 작동하도록보다 복잡한 시스템을 설계해야합니다.

그러나 항상 인터럽트를 설계하여 기능을 달성하는 데 필요한 최소한의 작업을 수행하고 다른 모든 것을 정규 처리로 지연 시키면 인터럽트가 다른 코드에 부정적인 영향을 미치지 않습니다. 필요한 경우 인터럽트 캡처 또는 해제 데이터를 가져 오거나 필요에 따라 출력을 설정 / 재설정 한 다음 메인 코드 경로가 인터럽트에 영향을 미치는 플래그, 버퍼 및 변수에주의를 기울여 긴 처리가 메인 루프에서 수행되도록합니다. 인터럽트보다는.

이를 통해 중단없는 코드 섹션이 필요할 수있는 매우 적은 상황을 제외하고 모두 제거해야합니다.


내가 일하고있는 상황을 더 잘 설명하기 위해 게시물을 업데이트했습니다 :)
Umberto D.

1
예제 코드에서는 필요하지 않을 때 특정 버튼 인터럽트를 비활성화하고 필요할 때 활성화하는 것을 고려할 것입니다. 이 작업을 자주 수행하는 것은 의도적으로 문제가되지 않습니다. 전역 인터럽트를 켜 두어 나중에 필요한 경우 나중에 코드에 다른 인터럽트를 추가 할 수 있습니다. 또는 상태 A로 갈 때 플래그를 재설정하고 그렇지 않으면 플래그를 무시하십시오. 버튼을 누르고 플래그를 설정하면 누가 신경 쓰나요? 상태 A로 돌아올 때까지 무시하십시오.
Adam Davis

예! 실제 디자인에서는 전역 인터럽트가 활성화 된 상태에서 LPM3 (MSP430)으로 자주 이동하고 LPM3을 종료하고 인터럽트가 감지되는 즉시 실행을 재개하기 때문에 해결책이 될 수 있습니다. 따라서 해결책은 코드의 두 번째 부분에보고 된 솔루션입니다. 필요한 상태의 수행 작업을 시작하자마자 인터럽트를 활성화하고 전환 블록으로 이동하기 전에 비활성화하십시오. 또 다른 가능한 해결책은 "활동 차단"을 떠나기 직전에 인터럽트를 비활성화하고 얼마 후 (언제?) 다시 활성화 할 수 있습니까?
Umberto D.

1

ISR에 플래그를 넣으면 기본적으로 인터럽트를 트리거 한 이벤트를 무시하므로 작동하지 않을 수 있습니다. 전역 적으로 인터럽트를 비활성화하는 것이 일반적으로 더 나은 선택입니다. 다른 사람들이 말했듯이, 당신은 이것을 자주하지 않아도됩니다. 단일 명령을 통해 수행되는 모든 읽기 또는 쓰기는 명령이 실행되거나 실행되지 않기 때문에 보호 될 필요가 없습니다.

많은 종류의 공유하려는 리소스에 따라 다릅니다. ISR에서 주 프로그램으로 (또는 그 반대로) 데이터를 공급하는 경우 FIFO 버퍼와 같은 것을 구현할 수 있습니다. 유일한 원 자성 작업은 읽기 및 쓰기 포인터를 업데이트하는 것이므로 인터럽트를 비활성화 한 상태에서 보내는 시간을 최소화합니다.


0

고려해야 할 미묘한 차이가 있습니다. 인터럽트 처리를 "지연"하거나 "무시"및 인터럽트를 선택할 수 있습니다.

종종 우리는 코드에서 인터럽트를 비활성화한다고 말합니다. 하드웨어 기능으로 인해 발생할 수있는 일은 인터럽트를 활성화하면 트리거됩니다. 이것은 인터럽트를 지연시키는 방법입니다. 시스템이 안정적으로 작동하려면 이러한 인터럽트 처리가 지연 될 수있는 최대 시간을 알아야합니다. 그런 다음 인터럽트가 비활성화 된 모든 경우가 더 짧은 시간 안에 완료되도록하십시오.

때때로 우리는 인터럽트를 무시하고 싶어합니다. 가장 좋은 방법은 하드웨어 수준에서 인터럽트를 차단하는 것입니다. 어떤 입력이 인터럽트를 생성해야하는지 말할 수있는 인터럽트 컨트롤러 또는 이와 유사한 것이 종종 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.