SNSへはこちら

AVRでアセンブリ(3) – PWMによるLチカ

またしてもLチカです。記事の投稿間隔がすごく短いですが、Lチカシリーズとしてまとめて投稿しています。
今回は、PWM を用いてLチカをしたいと思います。

コード

とりあえずコードを以下に。割り込みは使いません。

.include "../m328pdef.inc"
.device atmega328p ; state device name explicitly in order to check memory range

.cseg ; the start point of code
    ; IRQ vector table
    .org 0x0000 ; On RESET
        rjmp main

.def tmp = r16 ; alias tmp = r16

setup_TMR:
    ldi tmp, (0b11 << COM1A0) | (0b10 << WGM10) ; fast PWM for PB1, High on match
    sts TCCR1A, tmp
    ldi tmp, (0b100 << CS10) | (0b11 << WGM12) ; division by 256, match by OCR1A
    sts TCCR1B, tmp

    ; when counter matches OCR1A, PB1 is set in the next clock edge.
    ldi tmp, high(6250 - 1) ; per 200msec
    sts OCR1AH, tmp ; higher
    ldi tmp, low(6250 - 1) ; per 200msec
    sts OCR1AL, tmp ; lower

    ; when counter matches ICR1H, V=1 and counter will be reset in the next clock edge.
    ldi tmp, high(15625 - 1) ; per 500msec
    sts ICR1H, tmp ; higher
    ldi tmp, low(15625 - 1) ; per 500msec
    sts ICR1L, tmp ; lower

    ret

main:
    rcall setup_TMR
    sbi DDRB, PB1
    loop:
        rjmp loop

タイマーの設定

今回はタイマー1(TC1)を用います。

setup_TMR:
    ldi tmp, (0b11 << COM1A0) | (0b10 << WGM10) ; fast PWM for PB1, High on match
    sts TCCR1A, tmp
    ldi tmp, (0b100 << CS10) | (0b11 << WGM12) ; division by 256, match by OCR1A
    sts TCCR1B, tmp

    ; when counter matches OCR1A, PB1 is set.
    ldi tmp, high(6250 - 1) ; per 200msec
    sts OCR1AH, tmp ; higher
    ldi tmp, low(6250 - 1) ; per 200msec
    sts OCR1AL, tmp ; lower

    ; when counter matches ICR1H, V=1 and counter will be reset in the next clock.
    ldi tmp, high(15625 - 1) ; per 500msec
    sts ICR1H, tmp ; higher
    ldi tmp, low(15625 - 1) ; per 500msec
    sts ICR1L, tmp ; lower

    ret

まず、タイマーにはいろいろな動作がありますが、その一覧を以下に示します。

WGMxx では 高速 PWM 動作(14番)にしています。14番では、カウンタ値と OCR1A の比較によるピン出力ができ、タイマー周期は ICR1 で決定できます。15番にすると比較値とタイマー周期が一致するので、例えばピン状態のトグル等の設定をする場合には良いのでしょう。通常の PWM ですと比較値と周期は一致してはいけないので、今回は二者を別々に取っています。

続いて、比較値とタイマー周期の書き込みです。例によって ldists で行っていますが、ココで注意。この値は 16bit 値なので、AVR が 8bit マイコンであるという性質上、16bit を 8bit + 8bit で書き込むという動作をします。今回の場合はないですが、同様に 16bit 値を読み出すということもあるでしょう。その際、

  • 書き込みは上位から
  • 読み込みは下位から

という原則があるようです。確かに下位から書き込んでもうまく設定できてなさそうな雰囲気が出てきます。

本プログラムでは比較値を 200msec 分、周期を 500 msec 分としました。その際、high()low() というマクロがあるようです。今回のような場合に役立ちますね。

ということで、以上です。今回は新しいアセンブリ命令はありませんでした。