SNSへはこちら

Intel 8051マイコンを使う(STCマイコン)(1) – タイマー

続いてタイマーを使ってみようの回です。需要なんて無視して進めていきます(前回の記事のアクセス数が少ない...)。

ユーザマニュアルの入手

忘れていました。その前に、ユーザマニュアルを入手しましょう。STCmicro のデータシートは 間違いが多い ので、Intel のものを見ることがおすすめです。「intel 8051 user manual pdf」とでも検索すれば出ると思います。

タイマーを使ってみる

タイマーは Timer0 と Timer1 の2つがあるのですが(STC マイコンは 8052 ベースなので Timer2 もある)、動作モードが複数あります。
今回はカウントアップによるオーバーフロー と リロード値の設定ができる Mode 2 を扱います。

また、クロックは システムクロックを12分周したもの となっていますので、XTAL に 12MHz を入れた場合は 1MHz で動くんですね。

簡易1msecタイマー

とりあえず汎用のディレイを作りましょう。ソースコードを以下に示します。

#include <stdint.h>
#include <8052.h>
void ms_wait(uint32_t ms) {
    TMOD |= 0x02; // timer0, mode2
    TL0 = 256U - 100U; // counter
    TH0 = 256U - 100U; // reload
    TR0 = 1; // (TCON) timer0 enable
    for(uint32_t i=0; i<ms * 10UL; i++) {
        while( !TF0 ); // (TCON)
        TF0 = 0;
    }
    TR0 = 0; // (TCON) timer0 disable
}

Mode2 では、TLx がカウンター、THx がリロード値として働き、それぞれ 8bit 値です。このモード設定を TMOD で行っているというわけです。
タイマークロックは 1MHz なのでオーバーフローイベントを 1msec 毎に起こせば解決なのですが、リロード値を 1000 とするとこれは 8bit に収まりませんから、とりあえず 100 として、ループ側で対応をしています。
タイマーがオーバーフローを起こすと TFx がセットされます。割り込みを有効化するとベクタに飛び、自動的にフラグがクリアされるのですが、今回は割り込みを使用しないため手動でクリアしています。

使い方はこんな感じにすればいいと思います。

#include <stdint.h>
#include <8052.h>

int main(void) {
    while(1) {
        P1_0 ^= 1;
        ms_wait(1000);
    }
}

割り込みを起こす

この場合の初期化コードは上とそれほど変わりません。ただし、追加で以下の設定が必要です。

  • IE レジスタで全割り込み有効化
  • IE レジスタで Timer0 のオーバーフロー割り込み有効化

ペリフェラル側の設定は要らないようで(外部入力割り込みの場合は設定が必要、でも今回は関係ないですね)、比較的シンプルに書けます。
ここで、sdcc コンパイラ特有の書き方に注意です。割り込み関数は __interrupt(vector_num) ということを書かなければなりません。また、割り込み関数を別ファイルに書いた場合、main 関数があるファイル内にも __interrupt() 付きの関数プロトタイプ宣言を記載する必要があります

それでは初期化関数は以下です。

void timer0_irq_init(void) {
    EA = 1; // (IE) enable all interrupts
    ET0 = 1; // (IE) timer0 interrupt enabled
    TMOD = 0x02; // timer0, mode2
    TL0 = 256U - 100U; // counter
    TH0 = 256U - 100U; // reload
    TR0 = 1; // (TCON) timer0 enable
}

割り込み関数と main 関数はこんな感じでいいですかね。こちらも 1msec で LED をチカチカさせるようにしてあります。

int main(void) {
    timer0_irq_init();
    while(1);
}

void timer0_isr(void) __interrupt(1) {
    volatile static uint32_t cnt = 0;
    if( cnt++ == 1000U * 10 ) {
        P1_0 ^= 1;
        cnt = 0;
    }
}

これでタイマーの使い方は以上です。設定が簡単でしたね。
これは機能が少ないからであって、AVR のような CTC や、その他 PWM 等の機能はありません。