· 

ATtinyのTimer1をCTCモードで使う

ATtiny84をArduino化するとTimer0がdelayとかmillisとかに使われるので、Timer1の使い方をちょっとかじっておく。実はUSIを使ってみたい。USIはTimer0で自動的にstrobeできるけど、そのTimer0が使えないからTimer1の割り込みハンドラでソフトstrobeしようと思う。

でTimerの使い方ですが、所望のタイミングで割り込みを起こすために、

#define TNCT1_ini (0xFFFF-0x0100)

とかしておいて、タイマスタート直後に

TCNT1=TCNT1_ini;

ってする。で、割り込みハンドラでも

TCNT1=TCNT1_ini;

ってする。そすっと、カウント値0x0100で割り込みが起こせる。

、、、がこういう目的のためにCTC動作(TOP値を指定できる)ってのが存在し、本来こちらを使うべき。ところが、これがいままでうまくいったことがなくて避けて通ってたんだけど、いわゆる超ひまなので、そろそろまじめに取り組んでみる、、、っていうほどのことじゃない。

#include <avr/iotnx4.h>

#define PIN_CMPA_OUT (0x20)

volatile uint8_t stat;
ISR(TIM1_COMPA_vect){
    if((stat++)&0x01){
        PORTA|=PIN_CMPA_OUT;
    }else{
        PORTA&=~PIN_CMPA_OUT;
    }
}

void setup(void){
    stat=0;
    DDRA|=(PIN_CMPA_OUT);

    TCCR1A=0x00;
    OCR1A=0x0040;
    TCCR1B=0x08;
    TCCR1B|=0x01;/*タイマースタート*/
    TIMSK1=0x02;
}
void loop(void){
}

はいできたー。って簡単じゃん。実ははまりどころはTCCR1BのCSxを設定してタイマーとスタートするタイミングとOCR1AにTOP値をセットするタイミングです。OCR1Aなんていつでもやってよさそうなのに、上の例でTCCR1Aより前にOCR1Aに書き込むと正しく動作しません。TCCR1A=0x00;を省略しても動かなくなっちゃいます。むしろTCCR1BのCSxをセットしてスタートした後のほうが問題はない。

ちなみにOCR1A=0x0040だとパルス周期は実測16.6usなので割り込みは実測8.3usで発生しているらしい。

1/(8.3e-6/0x0040)=7,710,834

なので、タイマーに供給されているクロックがシステムクロックの8MHzってことがわかります。

なお、OCR1A=0x0020ってすると、、、ずっとHのままで動きません。処理が間に合わないってことなんでしょう、きっと。

さて、なんとOCR1AだけでなくOCR1Bってレジスタもあります。データシートによるとTOP値はOCR1Aなので中間値をOCR1Bにセットすれば、パルスのフェーズを覚えておかなくてもいいのでは、、、で、本筋ではない興味がわいてきたのでやってみる。

#include <avr/iotnx4.h>

#define PIN_CMPA_OUT (0x20)

ISR(TIM1_COMPA_vect){
    PORTA|=PIN_CMPA_OUT;
}
ISR(TIM1_COMPB_vect){
    PORTA&=~PIN_CMPA_OUT;
}

void setup(void){
    DDRA|=(PIN_CMPA_OUT);

    TCCR1A=0x00;
    OCR1B=0x0020;
    OCR1A=0x0040;
    TCCR1B=0x08;
    TCCR1B|=0x01;
    TIMSK1=0x06;
}
void loop(void){
}

これで、OCR1Bの0x0020で立下り、OCR1Aの0x0040で立ち上がるって書き方ができるので、パルスのフェーズを覚える必要がなくなり、ちょっとだけ速いパルスが作れます。余談でした。

 

コードの書き方がもうほんとArduinoじゃない。ただ専用ライターが不要でFT231Xから書き込めるってだけ。

ところで、このArduinoっぽくないコードの書き方で、_BV()を使ってレジスタへの書き込み値を作ることってあると思います。が個人的にはできるだけ使わないようにしています。

_BV()の定義は

#define _BV(bit) (1 << (bit))

となっています。これがマクロ展開されるので、毎回、このシフト演算を実行することになります。、、、時間がもったいない。

なので、ネットでよく見かける

PORTA|=(_BV(PA5)|_BV(PA4));

なんてのは

#define PA5bit 0x20

#define PA4bit 0x10

としておいて

PORTA|=(PA5bit|PA4bit);

ってすることにしています。

いや、さらに

PARTA|=0x30;

ってしちゃうことも個人的にはよくあります(可読性無視)。

 

ホビーでそんなに攻める必要はない、、、ですよね、すまソ(;´∀`)