2009-05-21 2 views
4

이 질문은 운영체제가없는 소형 마이크로 컨트롤러를 프로그래밍하는 것에 관한 것입니다. 특히 현재 PICS에 관심이 있지만 문제는 일반적입니다.타이머를 사용하여 시간을 유지하는 것은 임베디드 마이크로 컨트롤러를 중단합니다

타이머 인터럽트 코드 (타이머 화재마다 두 번째 말) :

... 
if (sec_counter > 0) 
    sec_counter--; 
... 

본선 코드 (비 인터럽트) :

나는 여러 번 시간을 유지하기위한 다음과 같은 패턴을 보았다

sec_counter = 500; // 500 seconds 

while (sec_counter) 
{ 
    // .. do stuff 
} 

주 코드는 카운터를 다양한 값 (초뿐 아니라)으로 설정하여 반복 할 수 있습니다.

주류 코드의 sec_counter에 대한 지정이 원자 적이지 않은 경우 여기에는 경쟁 조건이있는 것으로 보입니다. 예를 들어, PIC18에서 할당은 4 개의 ASM 문으로 변환됩니다 (그 시점에서 각 바이트를로드하고 그 전에 메모리 뱅크에서 오른쪽 바이트를 선택). 인터럽트 코드가 중간에 오면 최종 값이 손상되었을 수 있습니다.

이상하게도 할당 된 값이 256보다 작 으면 이라는 할당은 원자 번호가이므로 문제가되지 않습니다.

이 문제에 관해서는 맞습니까? 이러한 동작을 올바르게 구현하기 위해 어떤 패턴을 사용합니까? 이

  • 가 인터럽트를 사용하지 마십시오 꽤 아니라, 시작하고 폴링 별도의 타이머 - 후 sec_counter 및 수 있도록 각 할당하기 전에

    • 인터럽트를 비활성화 : 나는 몇 가지 옵션을 참조하십시오. 이것은 깨끗하지만 전체 타이머를 소모합니다 (이전의 경우 1 초 발사 타이머는 다른 목적으로도 사용될 수 있음).

    다른 아이디어?

  • 답변

    2

    PIC 아키텍처는 최대한 원초적입니다. 메모리 파일에 대한 모든 읽기 - 수정 - 쓰기 작업이 '원자 적'임을 보장합니다. 전체 읽기 - 수정 - 쓰기를 수행하는 데 4-clocks가 걸리지 만 모든 4- 클럭은 단일 명령어에서 소비되고 다음 명령어는 다음 4- 클럭 사이클을 사용합니다. 그것은 파이프 라인이 작동하는 방식입니다. 8- 클럭에서는 2 개의 명령이 파이프 라인에 있습니다.

    값이 8 비트보다 크면 PIC가 8 비트 시스템이고 더 큰 피연산자가 여러 명령어로 처리되므로 문제가됩니다. 그것은 원자 문제를 소개 할 것입니다.

    +1

    그건 뭐였지? 그는 2 바이트를 가지고 있다는 사실에 대해 이야기하고, 따라서 4 개 작업에 소요 된 8 비트 값을 카운터에 기록 할 때 아무 문제의 원인이 없다는 것을 내 경험 movlw MOVWF movlw MOVWF – SurDin

    +1

    이 정렬합니다. 문제는 워드의 HI 및 LO 부분 각각에 하나씩 두 개의 별도 쓰기 사이클을 사용하기 때문에 16 비트 값으로 시작됩니다. –

    +0

    이것은 PIC에만 해당되므로 - piclist에 질문하십시오. 네가 원하는 대답을 얻을 수있을거야. – sybreon

    1

    값을 쓰고 필요한 값이 가장 간단한 대안으로 보이는지 확인하십시오. 당신은 당신이 두 번 읽을 수있는 값을 읽어해야하는 경우 C.

    를 사용하는 경우

    do { 
    sec_counter = value; 
    } while (sec_counter != value); 
    

    BTW 당신은 ​​변수 휘발성을해야한다.

    do { 
        value = sec_counter; 
    } while (value != sec_counter); 
    
    +0

    재미있는 방법이지만, 인터럽트를 비활성화하는 것보다 의심 할 여지가 없습니다. 필자는 컴파일러 최적화가 어쨌든 비활성화 되었기 때문에 휘발성이 필요하다는 것을 확신하지 못합니다. –

    +0

    나는이 techique가 당신이 그것을 사용할 때 주석을 달았 으면 좋겠다. 왜냐하면 지키는 agaist가 하나의 "C 명령어"내에서 값을 변경하는 것을 방해하지 않는다는 것을 즉시 깨닫지 못하는 누군가에게 이상하게 보일 것이기 때문이다. – Martin

    +0

    인터럽트 비활성화가 도움이되지 않는 여러 메모리 매핑 레지스터에서 읽을 때도 작동합니다. – Dipstick

    0

    그럼 비교 어셈블리 코드는 어떻게 생겼습니까?

    아래쪽으로 계산된다는 점을 감안하면이 값은 0으로 간주되므로 MSB를 먼저 확인한 다음 LSB를 먼저 확인하는 것이 안전해야합니다. 손상이있을 수 있지만 0x100에서 0xff 사이의 중간에 오는지는 중요하지 않으며 손상된 비교 값은 0x1ff입니다.

    +0

    문제는 비교가되지 않는다고 생각합니다. 그것은 메인 라인 코드와 ISR이 동일한 변수에 쓰기 때문입니다. –

    1

    카운터를 설정하기 전에 확실히 인터럽트를 비활성화해야합니다. 추측 할 수 있듯이, 그것은 필요합니다. 하드웨어 레지스터 또는 ISR 방식에 영향을주는 소프트웨어 변수를 구성하기 전에 항상 인터럽트를 비활성화하는 것이 좋습니다. C로 작성하는 경우 모든 조작을 비 원자로 간주해야합니다. 생성 된 어셈블리를 너무 많이보아야 만한다면 어셈블리에서 C와 프로그램을 포기하는 것이 좋습니다. 제 경험으로 볼 때 이것은 드문 경우입니다.

    ISR: 
    if (countDownFlag) 
    { 
        sec_counter--; 
    } 
    

    카운터 설정 : 논의 된 문제에 대해서는

    이 내가 제안 것입니다

    // make sure the countdown isn't running 
    sec_counter = 500; 
    countDownFlag = true; 
    
    ... 
    // Countdown finished 
    countDownFlag = false; 
    

    당신은 여분의 변수를 필요로하는 기능에 모든 것을 포장하는 것이 좋습니다 :

    void startCountDown(int startValue) 
    { 
        sec_counter = 500; 
        countDownFlag = true; 
    } 
    

    이렇게하면 시작 방법을 추상화하고 필요한 경우 추악함을 숨길 수 있습니다. 예를 들어 메소드의 호출자에게 영향을 미치지 않고 하드웨어 타이머를 시작하기 위해 쉽게 변경할 수 있습니다.

    +0

    왜 인터럽트 플래그와 디스 에이블 모두가 필요합니까? 또는이 대안인가? –

    +0

    플래그와 인터럽트를 둘 다 필요로하지 않습니다. 그 중 하나가 충분합니다. 플래그를 사용하는 경우 카운터를 설정하기 전에 countDownFlag == false인지 확인하십시오. 기본적으로 이것은 당신이해야 할 일입니다 : 1) 카운트 다운 중지, 2) 카운터 설정, 3) 카운트 다운 시작 – kgiannakakis

    +1

    그러나 이것은 시계이므로 ISR 다운 틱을 느슨하게합니다 ... 비 결정적 방식으로 ... 우 – jpinto3912

    0

    주기를 변경하는 중일 수 있기 때문에 현재 타이머를 사용하는 방식으로는 초 단위로 계산하지 않습니다. 그러니 걱정하지 않으시면. 내 생각에 가장 좋은 방법은 가치를 읽은 다음 그 차이를 비교하는 것입니다. 더 많은 OP가 필요하지만 멀티 스레딩 문제가 없습니다. (타이머가 우선 순위를 가짐)

    시간 값에 대해 더 엄격한 경우 타이머가 0으로 카운트되면 자동으로 타이머가 비활성화됩니다. 타이머의 내부 카운터를 지우고 필요할 때 활성화하십시오.

    +0

    죄송합니다. 답변을 잘 모르겠습니다. 여기에 멀티 스레딩 및 멀티 코어가 없으며 베어 메탈 (Bare-on-Metal) 코드를 실행하는 작은 PIC18 만 있습니다. 또한, timing *은 합리적인 수준에 엄격합니다. –

    +0

    1.인터럽트를 사용하는 것은 2 개의 쓰레드가있는 간단한 버전입니다. 또한 같은 용어를 사용합니다. mainloop 스레드가 있고 인터럽트 스레드가 있으면 인터럽트 (뱅크 레지스터 저장 및 W 값 등)로 이동할 때 컨텍스트 전환이 필요합니다. 인터럽트로 이동하는 데 문제가 있지만 주 프로그램의 카운터에 쓰지 않으면 문제가 사라집니다. 2. 최대 2 * {타이머 길이}입니다. 더 필요하면 타이머 내부 카운트를 재설정해야합니다. – SurDin

    0

    main()에있는 코드 부분을 적절한 함수로 이동하고 ISR에서 조건부로 호출하도록합니다.

    또한 틱이 지연되거나 누락되지 않도록하려면이 타이머 ISR을 하이 프리오 인터럽트 (PIC18에는 두 가지 레벨이 있음)로 선택하십시오.

    1

    sec_counter 변수에 대한 액세스가 원 자성이 아니므로 코드에서이 변수에 액세스하고 결정 론적 동작을 원할 경우 액세스 후에 인터럽트 상태를 복원하기 전에 인터럽트를 비활성화하지 않는 방법은 없습니다. 이것은 아마도이 작업을 위해 HW 타이머를 할당하는 것보다 나은 선택 일 것입니다. (타이머가 잉여가 아니라면이 타이머를 사용하는 것이 좋습니다.)

    1

    마이크로 칩의 무료 TCP/IP 스택을 다운로드하면 거기에 타이머 오버플로를 사용하여 경과 시간을 추적하는 루틴이 있습니다. 특히 "tick.c"및 "tick.h". 해당 파일을 프로젝트에 복사하십시오.

    그 파일 안에는 파일의 작동 방식을 볼 수 있습니다.

    +0

    예, 기본적으로 인터럽트를 비활성화하고 ISR이 변경할 수있는 데이터에 액세스 할 때 다시 활성화합니다. –

    1

    원자가 256 개 미만인 것에 대해서는별로 궁금하지 않습니다. 8 비트 값을 이동하면 하나의 opcode가됩니다.

    PIC와 같은 마이크로 컨트롤러의 가장 좋은 솔루션은 타이머 값을 변경하기 전에 인터럽트를 비활성화하는 것입니다. 메인 루프에서 변수를 변경하고 원하는 경우 인터럽트 플래그의 값을 검사 할 수도 있습니다. 변수의 값을 변경하는 함수로 만들고 ISR에서도 호출 할 수 있습니다.

    +0

    가장 좋은 해결책은 RTC로 PIC를 가져 오는 것입니다. 나는 8 비트 시리즈에 어떤 것이 있는지 모른다. 그러나 16 비트 시리즈에있다. –

    +0

    바보 같은 BCD 변환 및 다른 말도 안 읽고 시간을 읽을 필요가없는 RTC 칩이있는 PIC가 있습니까? – supercat

    0

    하나의 접근법은 인터럽트가 바이트 변수를 유지하고 카운터가 적중 될 때마다 적어도 한 번 호출되는 다른 것입니다.

     
    ul big_ctr; 
    void poll_counter(void) 
    { 
        big_ctr += (ub)(now_ctr - big_ctr); 
    } 
    
    0

    아무도 문제의 해결하지 : 당신이 당신의 큰 카운터의 LSB와 동기화를 유지하기 위해 인터럽트의 카운터를 강제 괜찮다면,

     
    // ub==unsigned char; ui==unsigned int; ul==unsigned long 
    ub now_ctr; // This one is hit by the interrupt 
    ub prev_ctr; 
    ul big_ctr; 
    
    void poll_counter(void) 
    { 
        ub delta_ctr; 
    
        delta_ctr = (ub)(now_ctr-prev_ctr); 
        big_ctr += delta_ctr; 
        prev_ctr += delta_ctr; 
    }

    약간의 변화 : 그런 짓을 (예를 들어, 타이머를 멀티 바이트 하드웨어 레지스터를 읽고 . 타이머는 g. 당신은 수도 이월 및 두 번째 바이트를 증가 당신이 그것을 읽는 동안.

    을가 0x0001ffff의 말 당신은 그것을 읽을 수 et 0x0010ffff 또는 0x00010000.

    16 비트 주변 레지스터는 코드에 휘도가입니다.

    휘발성 "변수"에 대해서는 이중 읽기 기법을 사용합니다.

    do { 
         t = timer; 
    } while (t != timer); 
    
    관련 문제