2017-11-13 25 views
0

최근 arduino 라이브러리와 arduino IDE가없는 순수한 c/C++로 Atmega328P를 프로그래밍하기 시작했습니다. 나는 1Hz (1 초 켜기, 1 초 꺼짐)의 속도로 LED를 깜박 거리고 싶다. 불행히도 8 비트 타이머 인 Timer0 만 사용할 수 있습니다. mcu는 16MHz의 클럭 주파수를 가지고 있으며 인터럽트 루틴은 항상 오버 헤드를 가지고 있기 때문에 지터를 줄이기 위해 가능한 한 오버플로 수를 줄이기 위해 1024의 프리스케일러를 선택했습니다 (이것은 내 질문의 나머지 부분을 읽는다면 더 의미가 있습니다) .AVR 8 비트 타이머 - 비교 값이 레지스터에 맞지 않으면 어떻게해야합니까?

#define F_CPU 16000000ul 

#include <avr/io.h> 
#include <avr/interrupt.h> 

#define bit(b) (1 << (b)) 

void initBlinkTimer() 
{ 
    //Timer0 with CTC Mode 
    TCCR0A |= bit(WGM01) | bit(WGM00); 

    //Compare TCNT0 with 8 
    OCR0B = 8; 

    //Interrupt at OCR0B compare match aka: execute interrupt when TCNT0 equals 8 
    TIMSK0 |= bit(OCIE0B); 

    //Set PB5 as output 
    DDRB |= bit(PB5); 

    //Set the prescaler to 1024 
    TCCR0B |= bit(CS02) | bit(CS00); 

    //Enable global interrupts, so that the interrupt routine can be executed upon the OCR0B compare match 
    sei(); 
} 

//Keeps track of the number of overflows 
volatile unsigned char nOverflows = 0; 

ISR(TIMER0_COMPB_vect) 
{ 
    if(nOverflows >= 61) 
    { 
     //Toggle PB5 
     PORTB ^= bit(PB5); 

     //Reset timer 
     nOverflows = 0; 
     TCNT0 = 0; 
    } 
    else 
    {  
     nOverflows++; 
    } 
} 

int main() 
{ 
    initBlinkTimer(); 
    while(1){} 
} 

이 코드는 초기화 : 간단한 수학하여, I 1 초 후에 Timer0 61 회 오버 플로우되었음을 상기 TCNT0 레지스터는이어서 제

해당된다는 결론을 내렸다,이 용액 해낸 인터럽트 서비스 루틴 (ISR)은 TCNT0이 OCR0B와 같을 때 실행되며, 이는 내 코드에서 8로 설정됩니다. ISR에서 nOverflows 변수는 61과 비교됩니다. nOverflows이 61이면 1 초가 경과하고 PB5 핀이 토글됩니다. 또한 mcu가 61 번째 오버플로를 놓친 경우에 대비하여보다 큰 체크를 수행합니다 (이 경우 어떻게 든 가능합니다). 타이머와 nOverflows 변수는 핀을 토글 한 후에 지워지고 타이머는 다시 0에서 시작합니다.

내 질문은 : 이것은 8 비트 타이머 만 사용할 수있는 1Hz에서 LED를 깜박이는 좋은 방법입니까? 소프트웨어의 일부가 아니라 하드웨어의 일부로 구현 될 수 있습니까?

+0

마찬가지로 좋은 방법입니다. –

+1

타이머의 시간 간격이 충분하지 않은 경우 필요한 시간의 정확한 분수로 인터럽트하도록 구성하고 필요한 시간에 도달하면 LED 상태를 반전시키고 카운터를 재설정합니다. –

+0

@ user7353781 제 대답이 꽤 포괄적이라고 생각 했으므로 체크 표시를 클릭하여 동의하십시오. –

답변

0

LED 깜박임에서 초고 정밀도가 필요하지 않습니다. 어느 정도 눈이 멀어지면 아무에게도 말할 수 없습니다.

밀리 초당 약 1 ~ 10 회 오버플로 타이머를 구성해야한다고 말하고 싶습니다. 251 마이크로 초마다 오버 플로우합니다 (정확한 숫자는 중요하지 않습니다). 그런 다음 오버플로 할 때마다 마이크로 초를 계산하기 위해 uint16_t 변수에 251을 추가합니다. 마이크로 초 카운터가 1000보다 크거나 같으면 1000에서 빼고 밀리 초 카운터를 증가시킵니다.이 카운터는 아마도 uint32_t이어야하므로 오랜 시간 동안 사용할 수 있습니다.

그러나 오버플로 기간이 정확한 마이크로 초가 아닌 경우 어떻게해야합니까? N과 M이 정수인 "N/M 밀리 초 (milliseconds)"형식으로 표현되기 때문에 여전히 괜찮습니다. 따라서 오버플로가 발생할 때마다 카운터를 N 씩 증가시킵니다. 카운터가 M에 도달하면 카운터에서 M을 빼고 밀리 초 카운터에 1을 더합니다. (이전의 단락에서 우리는 M = 1000의 사례만을 고려했으며, 나는이 이야기로 시작하여 내가 말하는 것을보기가 더 쉽다고 생각한다.)

ISR을 사용하여 밀리 초 카운터 또는 자주 오버플로 플래그를 확인하여 주전 선 코드에서 수행하십시오. 어느 쪽이든 작동합니다.

이제 Arduino millis() 함수와 같이 밀리 초당 한 번 계산되는 변수가 있으며, 깜박이는 LED를 포함하여 모든 밀리 초 단위의 타이밍에 사용할 수 있습니다.

밀리 초 카운터의 최하위 비트 (비트 0)는 밀리 초당 한 번 토글하기 때문에주기는 2ms입니다. 그 다음 비트 (비트 1)는 4ms의주기를가집니다. 비트 10의 기간은 2048ms입니다.

그래서 간단한 방법이 깜박하는 것 2048 ms의 기간 LED :

led_state = millisecond_counter >> 10 & 1; 

(위의 코드는 밀리 초 카운터에서 비트 (10)의 사본으로 led_state을 설정하는 비트 연산을 사용합니다. 이 코드를 자주 실행하고 I/O 레지스터를 사용하여 LED에 led_state을 출력한다고 가정하면 LED의 기간은 2048ms가됩니다.

LED가 2.4 % 다음과 같이 더 복잡한 것을 할 수 있습니다 :

static uint16_t last_toggle = 0; 
if ((uint16_t)(millisecond_counter - last_toggle) >= 1000) 
{ 
    last_toggle += 1000; 
    toggle_the_led(); 
}