2017-11-13 11 views
0

最近、純粋なc/C++で、arduinoライブラリとarduino IDEを一切使わずにAtmega328Pをプログラミングし始めました。 LEDを1Hz(1秒オン、1秒オフ)の速度で点滅させたい。残念ながら、私は8ビットタイマTimer0しか使用できません。 mcuは16MHzのクロック周波数を持っています。割り込みルーチンが常にオーバーヘッドを持っているので、ジッタを減らすためにできるだけオーバーフローの数を減らすために1024のプリスケーラを選択しました(これは私の質問の残りの部分を読んでください) 。簡単な数学を使用して、私は1秒後に、Timer0は61回オーバーフローしたこととTCNT0レジスタはその後8AVR 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){} 
} 

このコードは、初期化プリスケーラが1024の8ビットCTCタイマです。TCNT0がOCR0Bに等しいときに割り込みサービスルーチン(ISR)が実行されます。これはコードで8に設定されています。 ISRでは、nOverflowsの変数が61と比較されます。nOverflowsが61の場合、1秒が経過し、PB5ピンがトグルされます。 mcuが61番目のオーバーフローを逃した場合(これは何らかの形でこの場合可能です)、より大きいチェックを実行します。タイマとnOverflows変数はピンをトグルした後にクリアされ、その後タイマは再びゼロから始まります。

私の質問は:1HzでLEDを点滅させるには良い方法ですか、8ビットタイマだけが利用できるのですか?これの一部をソフトウェアではなくハードウェアで実装することはできますか?

+0

これは他の方法と同じように便利です.... –

+1

タイマーに十分な時間がない場合は、必要な時間の正確な分だけ割り込みが発生するように設定し、必要な時間に達すると、LED状態を反転してカウンタをリセットします。 –

+0

@ user7353781私の答えはかなり包括的だと思ったので、チェックマークをクリックしてそれを受け入れることを検討してください。 –

答えて

0

LEDの点滅で超高精度を必要としません。少しでも外れていると誰も目にすることができません。

私はミリ秒あたり約1から10回オーバーフローするようにタイマーを設定する必要があります。 251マイクロ秒ごとにオーバーフローするとします(正確な数は関係ありません)。それがオーバーフローするたびに、マイクロ秒をカウントするために変数uint16_tに251を追加します。マイクロ秒カウンタが1000以上の場合は、1000を減算してミリ秒カウンタを増やします。これはおそらくuint32_tである必要がありますので、時間をかけて長時間使用することができます。

しかし、オーバーフロー期間が正確なマイクロ秒数でない場合はどうなりますか? NとMが整数である "N/Mミリ秒"の形式で表現されている可能性があるので、これはまだ問題ありません。カウンタがMに達すると、カウンタからMを減算し、ミリ秒カウンタに1を加算します。 (前のパラグラフではM = 1000のケースしか考えていませんでしたが、私が話していることが分かりやすいと思います)

ISRを使用してミリ秒オーバーフローフラグを頻繁にチェックすることによって、メインラインコード内で実行してください。どちらの方法でも動作します。

これでArduino millis()関数のように1ミリ秒に1回カウントアップする変数があり、点滅するLEDを含むあらゆる種類のミリ秒スケールのタイミングに使用できます。

ミリ秒カウンタの最下位ビット(ビット0)は、ミリ秒ごとに1回トグルするため、その周期は2ミリ秒です。それからの次のビット(ビット1)は4msの周期を有する。ビット10の周期は2048msです。

だから2048ミリ秒の期間を使用してLEDを点滅させるための簡単な方法は次のようになります

led_state = millisecond_counter >> 10 & 1; 

(上記のコードはミリ秒カウンタからのビット10のコピーであることled_stateを設定するためにビット演算を使用します。このコードを頻繁に実行し、I/Oレジスタを使用してled_stateをLEDに書き出すと、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(); 
} 
関連する問題