第 9 回 割り込み

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

今までのプログラム演習では、一定の時間を待つのに「ビジーウェイト」とい う手法で、何もしないことを何万回と繰り替えして一定の時間を作りました。

この様な手法はどのコンピュータでも使える基本的な手法ですが、何もしてい ないときに電力を抑えたり、あるいは、複数のプログラムを平行して動かした り、外部と協調して動作させるなどには向きません。

そこで、今回はCPUの機能として重要な割り込みの技術を紹介します。 まずは、それに付随して活用する sleep について学びます。

この割り込みを使用して、CPU を止めたり、一定の時間間隔をタイマー で計ってプログラムを動かします。

9-1. プログラムの待機

一般的に様々なCPUでプログラムの実行を止める仕組みが用意されています。 そして、止めている間の状態や、再開する条件なども設定できるものがあり ます。

には sleep 命令があります。 この sleep 命令でどのような状態になるかはプログラム内部で 通りに指定することができます( )。 外部からの割り込み信号が来るまでほとんどの機能を停止するモードから、単 に CPU を止めるだけのモードもあります。 今回は、すべての I/O が存続していながら CPU だけを止める Idle モードを選びます。

Idle モードを利用するには、以下の手順が必要です。

  1. 特定のI/Oポートに設定値を書き込む
  2. 特定のI/Oポートに Sleepを有効にする値を書き込む
  3. sleep命令を実行

マシン語

AtMega328P の 9章 Power Management and Sleep Modes に記載されています。

  1. Sleep Mode Control Register(SMCR)の SM2,SM1,SM0 をともに 0 にして Idle モードを指定する
  2. 同じく SMCRの SE を 1 にして、 Sleep 命令を有 功にする
  3. 必要に応じて sleep 命令を指定し、 Idle モードに入る

AtMega32u4 の 7章 Power Management and Sleep Modes に記載されています。

  1. Sleep Mode Control Register(SMCR)の SM2,SM1,SM0 をともに 0 にして Idle モードを指定する
  2. 同じく SMCRの SE を 1 にして、 Sleep 命令を有 功にする
  3. 必要に応じて sleep 命令を指定し、 Idle モードに入る

AtMega4809 の 11章 SLPCTRL - Sleep Controller に記載されています。

  1. SLPCTRL の SMODE[1:0] を 0 にして Idle モードを指定する
  2. 同じく SLPCTRL の SEN を 1 にして、 Sleep 命令を有 功にする
  3. 必要に応じて sleep 命令を指定し、 Idle モードに入る

例えば、内蔵LEDを光らせて止まるプログラムは次のように書ける。

例9-1


;************
;*  例9-1 *
;************
.cseg
.org    0x0000
        rjmp    reset
.org    INT_VECTORS_SIZE
reset:
        ldi     r16,high(RAMEND)
        out     SPH,r16
        ldi     r16,low(RAMEND)
        out     SPL,r16

.equ    onb     = 0b00111111
.equ    ond     = 0b11000000
        ldi     r16,onb
        out     ddrb, r16
        ldi     r16,ond
        out     ddrd, r16

        in      r16,smcr
        cbr     r16,1<<sm0
        cbr     r16,1<<sm1
        cbr     r16,1<<sm2
        sbr     r16,1<<se
        out     smcr,r16

main:
        ldi     r16,0b00110100 //10, 12, 13
        out     portb,r16
        sleep
        ldi     r16,0b00111111 //8,9,10,11,12,13
        out     portb,r16
.exit

;************
;*  例9-1 *
;************
.cseg
.org    0x0000
        rjmp    reset
.org    INT_VECTORS_SIZE
reset:
        ldi     r16,high(RAMEND)
        out     SPH,r16
        ldi     r16,low(RAMEND)
        out     SPL,r16

.equ    onb     = 0b11110000
.equ    onc     = 0b10000000
.equ    ond     = 0b11000000
.equ    one     = 0b01000000
        ldi     r16,onb
        out     ddrb, r16
        ldi     r16,onc
        out     ddrc, r16
        ldi     r16,ond
        out     ddrd, r16
        ldi     r16,one
        out     ddre, r16

        in      r16,smcr
        cbr     r16,1<<sm0
        cbr     r16,1<<sm1
        cbr     r16,1<<sm2
        sbr     r16,1<<se
        out     smcr,r16

main:
        sbi     portb,1<<6 //10
        sbi     portd,1<<6 //12
        sbi     portc,1<<7 //13
        sleep
        sbi     portb,1<<4 //8
        sbi     portb,1<<5 //9
        sbi     portb,1<<7 //11
.exit

;************
;*  例9-1 *
;************
.cseg
.org    0x0000
        rjmp    reset
.org    INT_VECTORS_SIZE
reset:
        ldi     r16,low(RAMEND)
        out     CPU_SPL,r16
        ldi     r16,high(RAMEND)
        out     CPU_SPH,r16 

.equ    ona     = 0b00000010
.equ    onb     = 0b00000011
.equ    one     = 0b00001111
.equ    onf     = 0b00010000

        lds     r16,PORTA_DIR
        ori     r16,ona
        sts     PORTA_DIR,r16
        lds     r16,PORTB_DIR
        ori     r16,onb
        sts     PORTB_DIR,r16
        lds     r16,PORTE_DIR
        ori     r16,one
        sts     PORTE_DIR,r16
        lds     r16,PORTF_DIR
        ori     r16,onf
        sts     PORTF_DIR,r16


        lds     r16,SLPCTRL_CTRLA
        ori     r16,SLPCTRL_SMODE_IDLE_gc 
        ori     r16,SLPCTRL_SEN_bm
        sts     SLPCTRL_CTRLA,r16

main:
        lds     r16,PORTB_OUT
        ori     r16,0x00000010 //10
        sts     PORTB_OUT,r16
        lds     r16,PORTE_OUT
        ori     r16,0x00000110 //12, 13
        sts     PORTE_OUT,r16
        sleep
        lds     r16,PORTB_OUT
        ori     r16,0x00000011 //9,10
        sts     PORTB_OUT,r16
        lds     r16,PORTE_OUT
        ori     r16,0x00001111 //8,1112, 13
        sts     PORTE_OUT,r16
.exit

C言語

C言語では次のように、ヘッダファイルを読み込み、設定関数を呼び、 sleep関数を呼ぶことで実現します。 この手順は、同じ系列のCPUであれば、共通化されています。

  1. avr/sleep.h をインクルードする
  2. set_sleep_mode で スリープモードの設定をする。 Idle モードにするには set_sleep_mode( SLEEP_MODE_IDLE) を呼び出す。
  3. sleep_enable() を呼び、sleepを有効にする
  4. sleep_cpu()呼び出しで実際に Sleep する

例9-2


#include <avr/sleep.h>
const byte out7seg[] = {6,7,8,9,10,12,13,0xff};
void setup(){
  for(byte i=0; out7seg[i]!=0xff; i++){
    pinMode(out7seg[i], OUTPUT);
  }
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
  digitalWrite(8,HIGH);
  digitalWrite(10,HIGH);
  sleep_cpu();
  digitalWrite(9,HIGH);
}
void loop(){
}

9-2. 割り込み

外部の刺激などに対して、CPU があらかじめ指定した番地を呼び出すのを 割り込みと言います。 呼び出される番地を割り込みベクタと言います。 AVR では各種の割り込みそれぞれに対して、用意された番地のプログラムを呼 び出します。 これは先頭番地から1命令分(2byte)の領域だけ連続した領域に設定されていますので、直接、割り込みベ クタに処理プログラムを書くことはできません。 そのため、通常はここにはジャンプ命令のみを書いて、割り込み処理プログラ ムへ処理を移します。

ここでは例題として演習問題6-5 を改造して、タイマ割り込みにより 一定周期でカウンタを増やすプログラムを作ることを考えます。

ATmega328p に内蔵されているタイマ/カウンタ TC2 は 8bit ながら複雑な機能を持っ ています。 今回は、ほぼ 1 秒に一回カウンターを増やして値を表示するだけの処理をす るのが目標です。

ATmega32u4 に内蔵されているタイマ/カウンタ TC0 は 8bit ながら複雑な機能を持っ ています。 今回は、ほぼ 1 秒に一回カウンターを増やして値を表示するだけの処理をす るのが目標です。

ATmega4809 に内蔵されているタイマ/カウンタ TCA は複雑な機能を持っ ています。 今回は、ほぼ 1 秒に一回カウンターを増やして値を表示するだけの処理をす るのが目標です。

これを実現するには、一定時間毎にカウンターを 1 増やして値を表示するプ ログラムを作る一方、初期設定をした後、カウンターを表示してタイマーをス タートした後 sleep するプログラムが必要になります。 なお、sleep中に割り込みが発生すると割り込みを処理した後、 sleep 命令の 次に制御が戻りますので、割り込み以外の処理をしない場合は、 sleep 命令 だけを実行する無限ループを作ります。

マシン語

今回は単純なモードとして normal モードを使用します(17.7.1 p.121)。 各モードの設定は pp.127-134 のレジスタを参照します。

TC2 は 0x00 から数えて 0xff まで加算した後、0x00に戻る際にオーバフロー 割り込みを発生させます。 つまり、割り込みベクタは TOV2=0x0009 になります。 またPrescalar を使用して、ほぼ1秒に一回割り込みがかかるようにします。 そのための設定ですが、以下のようになります。

レジスタ名意味
TCCR2A0b00000000normalモードはすべて0
TCCR2B0b00000111クロックの 1024分の1で割り込 み発生(モード設定がTCCR2Aと別れていることに注意)
TIMSK20b00000001オーバーフロー検知だけ有 効

Arduino は 16MHz で動作してます。 そのため、 1024 で分周した後、さらに 256 回に一回の割合で割り込みが発 生するとすると、割り込みは 1024*256/16M ≒ 0.016 秒に一回発生することに なります。

今回は単純なモードとして normal モードを使用します(19.7.2 p.134)。 各モードの設定は pp.141-151 のレジスタを参照します。

TC0 は 0x00 から数えて 0xff まで加算した後、0x00に戻る際にオーバフロー 割り込みを発生させます。 つまり、割り込みベクタは TOV2=0x0009 になります。 またPrescalar を使用して、ほぼ1秒に一回割り込みがかかるようにします。 そのための設定ですが、以下のようになります。

レジスタ名意味
TCCR2A0b00000000normalモードはすべて0
TCCR2B0b00000111クロックの 1024分の1で割り込 み発生(モード設定がTCCR2Aと別れていることに注意)
TIMSK20b00000001オーバーフロー検知だけ有 効

Arduino は 16MHz で動作してます。 そのため、 1024 で分周した後、さらに 256 回に一回の割合で割り込みが発 生するとすると、割り込みは 1024*256/16M ≒ 0.016 秒に一回発生することに なります。

今回は単純なモードとして normal モードを使用します(19.7.2 p.134)。 各モードの設定は pp.141-151 のレジスタを参照します。

TC2 は 0x00 から数えて 0xff まで加算した後、0x00に戻る際にオーバフロー 割り込みを発生させます。 つまり、割り込みベクタは TOV2=0x0009 になります。 またPrescalar を使用して、ほぼ1秒に一回割り込みがかかるようにします。 そのための設定ですが、以下のようになります。

レジスタ名意味
TCCR2A0b00000000normalモードはすべて0
TCCR2B0b00000111クロックの 1024分の1で割り込 み発生(モード設定がTCCR2Aと別れていることに注意)
TIMSK20b00000001オーバーフロー検知だけ有 効

Arduino は 16MHz で動作してます。 そのため、 1024 で分周した後、さらに 256 回に一回の割合で割り込みが発 生するとすると、割り込みは 1024*256/16M ≒ 0.016 秒に一回発生することに なります。

例9-3

演習 7-5 のプログラムを改造します。 counter という変数を用意し、次のサブルーチンを用意します。

  1. counter の値を表示するサブルーチン
  2. counter を 1 増やし、 counter の値を表示するサブルーチン

このような準備をした後、次のプログラムを作ります。

  1. sleepの設定をします
  2. 割り込みの設定をします。
  3. counterを 0 にし、LED を光らせます。
  4. sleep を含む無限ループに入ります。

また、割り込みの処理は次のようにします。

  1. まず OVF2addr 番地から割り込み処理のプログラムに飛ぶようにします。
  2. counter を1増やし、 LEDを光らせます
  3. reti 命令により割り込みを終了します。

プログラムは次のようになります


;**********
;*  例9-3 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r18
.def    pattern = r17

    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    inccounter
    rcall    dispcounter
    reti
.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

;**********
;*  例9-3 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r18
.def    pattern = r17

    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    inccounter
    rcall    dispcounter
    reti
.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

;**********
;*  例9-3 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r18
.def    pattern = r17

    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    inccounter
    rcall    dispcounter
    reti
.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

例9-4

例9-3ではカウンタの動きが速すぎます。 Prescaler を使用しても最大で 1/256 にしかなりません。 クロック 16MHz では 1 マシンサイクルが 0.25μ秒なので、カウンタ一つ 増やす間隔は 0.25×256×256μ秒 ≒ 0.016 秒に一回になってます。 これを大体 1 秒の間隔にするにはさらに 61 倍程度遅くする必要があります。

そこで大体 1 秒毎に増えるカウンタを作りましょう。 そのためには割り込みがかかっても 61 回に一回しか反応しないようにします。 その機能を持つサブルーチンの名前を Postscalerと呼ぶ事にしま す。 これは新たにレジスタを使用して、 61 回中 60 回まではレジスタを加算する だけで戻り、61回目にレジスタを 0 にして、指定のサブルーチンを呼ぶもの です。


;**********
;*  例9-4 *
;**********

.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r19
.def    pattern = r17
.def    pcounter = r18
.equ    ptime = 61
    ldi    pcounter,ptime
    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec        pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi        pcounter,ptime
    rcall    inccounter
    rcall    dispcounter
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

;**********
;*  例9-4 *
;**********

.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r19
.def    pattern = r17
.def    pcounter = r18
.equ    ptime = 61
    ldi    pcounter,ptime
    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec        pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi        pcounter,ptime
    rcall    inccounter
    rcall    dispcounter
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

;**********
;*  例9-4 *
;**********

.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    counter = r19
.def    pattern = r17
.def    pcounter = r18
.equ    ptime = 61
    ldi    pcounter,ptime
    clr    counter    
    rcall    dispcounter
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec        pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi        pcounter,ptime
    rcall    inccounter
    rcall    dispcounter
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
    lpm    r16,Z
    outport    onb,portb
    outport    ond,portd
    ret
    

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdata:
    .db    0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)

.exit

演習9-1

LED の小数点を約 0.5 秒程度で点滅させなさい。 さらに、 スイッチを押す度に LED の数字が増減するようにしな さい。

C言語

C言語で割り込みを行う場合、ライブラリーを使うのが一般的なようです。 Arduino の「スケッチ→ライブラリをインクルード→ライブラリを管理」 を選び、検索欄に TimerInterrupt_Generic を入れると、 TimerInterrupt_Generic をインストールできます。 これは、 #define USE_TIMER_1 true などで、使用するタイマーを指定した後、 #include "TimerInterrupt_Generic.h" でヘッダファイルを読み込みます。 そして、setup で ITimer1.init(); で使用するタイマーを起動し、 ITimer1.attachInterruptInterval(時間間隔,呼び出し関数) で一定間隔で関数を呼び出すようになります。

例9-5

例9-6と同様に一定時間毎に別のパターンを表示するプログラムを、割り込み と sleep を使用して書いたのが以下のプログラムです。


/************
*  例9-5 *
************/
#include <avr/sleep.h>
#define USE_TIMER_1     true
#include "TimerInterrupt_Generic.h"

  // put your setup code here, to run once:
const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
const byte pattern[]={
            0b11011110, //0
            0b10010000, //1
            0b11001101, //2
            0b11011001, //3
            0b10010011, //4
            0b01011011, //5
            0b01011111, //6
            0b11010000, //7
            0b11011111, //8
            0b11011011, //9
            0b11010111, //A
            0b00011111, //b
            0b01001110, //C
            0b10011101, //d
            0b01001111, //E
            0b01000111 //F
};
byte counter;
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  counter=0;
    ITimer1.init();
  ITimer1.attachInterruptInterval(1000, vect);
  dispcounter();
}

void dispcounter(){
  for(byte i=0; i<8;i++){
    digitalWrite(out7seg[i],(pattern[counter]&1<<i)!=0);
  }
}
void inccounter(){
  counter++;
  if(counter>=sizeof(pattern)){
    counter=0;
  }
}
void vect(){
  inccounter();
  dispcounter();
}
void loop() {
  sleep_cpu();
}

演習9-2

LED の小数点を約 0.5 秒程度で点滅させなさい。 さらに、 スイッチを押す度に LED の数字が増減するようにしな さい。

9-3. LED の明るさを変える(DUTY 比)

LED の明るさを変えるには二つの方法があります。 今回は DUTY 比のコントロールについて考えます。 この方法は、早い周期で LED を点けたり消したりすることで、平均出力を下げる ことにより、人間の目には暗く見えるようにすることです。 この時、全体の中で ON になっている比率を DUTY 比と呼びます。

例9-6

例 5-3 を思い出して下さい。 これは、PORTB0 から PORTB7 まで順番に点灯してくというプログラムでした。 その演習では、一つのポートの LED を点灯してから、次のポートを点灯させ るまで時間待ちをしました。 そこで、この waitを外したものを動かしてみましょう。 単純で改造がしやすい 5-3 2 のプログラムを使用します。 改造した物が次になります。 なお、全部消灯するパターンも取り除いています。


;**********
;*  例9-6 *
;**********
.cseg
.org    0x0000
    rjmp    reset
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16
        
.macro    clrport
    in    r16,@0
    andi    r16,~@1
    out    @0,r16
.endmacro
.macro setport
    in    r16,@0
    andi    r16,~@1
    ori    r16,@2
    out    @0,r16
.endmacro
main:
    setport portb,onb,0b00000001
    setport portb,onb,0b00000011
    setport portb,onb,0b00000111
    setport portb,onb,0b00001111
    setport portb,onb,0b00011111
    setport portb,onb,0b00111111
    setport portd,ond,0b01000000
    setport portd,ond,0b11000000
    clrport    portd,ond
    rjmp    main
.exit

;**********
;*  例9-6 *
;**********
.cseg
.org    0x0000
    rjmp    reset
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16
        
.macro    clrport
    in    r16,@0
    andi    r16,~@1
    out    @0,r16
.endmacro
.macro setport
    in    r16,@0
    andi    r16,~@1
    ori    r16,@2
    out    @0,r16
.endmacro
main:
    setport portb,onb,0b00000001
    setport portb,onb,0b00000011
    setport portb,onb,0b00000111
    setport portb,onb,0b00001111
    setport portb,onb,0b00011111
    setport portb,onb,0b00111111
    setport portd,ond,0b01000000
    setport portd,ond,0b11000000
    clrport    portd,ond
    rjmp    main
.exit

;**********
;*  例9-6 *
;**********
.cseg
.org    0x0000
    rjmp    reset
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16
        
.macro    clrport
    in    r16,@0
    andi    r16,~@1
    out    @0,r16
.endmacro
.macro setport
    in    r16,@0
    andi    r16,~@1
    ori    r16,@2
    out    @0,r16
.endmacro
main:
    setport portb,onb,0b00000001
    setport portb,onb,0b00000011
    setport portb,onb,0b00000111
    setport portb,onb,0b00001111
    setport portb,onb,0b00011111
    setport portb,onb,0b00111111
    setport portd,ond,0b01000000
    setport portd,ond,0b11000000
    clrport    portd,ond
    rjmp    main
.exit

実行すると、 LED の明るさが揃ってないことがわかります。

C言語版

const byte out[]={6,7,8,9,10,11,12,13,0xff};
void setup() {
  for(byte i=0;out[i]!=0xff;i++){
    pinMode(out[i], OUTPUT);
  }
}
void clear(){
  for(byte i=0;out[i]!=0xff;i++){
    digitalWrite(out[i], LOW);
  }
}
byte i=0;
void loop() {
  digitalWrite(out[i++],HIGH);
  delay(1);
  if(out[i]==0xff){
    i=0;
    clear();
  }
}

この場合、PORTB0 の duty 比は 100% です。一方、 PORTD7 の duty 比は約1/8 = 12.5% になります。 但し、実験してみれば分かるように duty 比の高い LED の明るさの違いはほ とんど分かりません。 人間の目は対数的な特性があるため、指数関数的な変化でないとはっきり把握 できません。 つまり、 duty 比を連続して変化させる時は、比率を連続的に変化させても効 果が薄いことに注意します。

例9-7

小数点の明るさを変化させることを考えましょう。 DUTY比を変えるには小数点だけを素早く ON, OFF させます。 つまり小数点は常に同じ値ではなく、割り込みにより On/OFF を繰り返します。 つまり、タイマ割り込みを使用し、 duty 比が 25% になるように 4 回に 1 回だけ set し、残りは clear するようにします。 なお比較のために、 7SEG LED は小数点以外は全て点灯させておきます。

プログラムにおいて、レジスタ crate の値は割り込みがかかる度に countrate, countrate-1, ... ,3,2,1, countrate, countrate-1, ... ,3,2,1, と周期的に変化して行きます。 そして、その度に あらかじめ W レジスタに入れた dutyrate の値から引き算します。 すると、crate - dutyrate < 0 となる時は C フラグが 0 になり、そうでなければ 1 になります。 ここで、 C フラグが 0 の時、つまり 1 ≤ crate < dutyrate の時に、 小数点を 光らせ、それ以外の時に小数点を消灯することで、 DUTY 比 = (dutyrate - 1)/countrate を実現します。 25% にするには countrate = 4, dutyrate = 2 とします。


;**********
;*  例9-7 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    push    r16
    in        r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r17
    .equ    dcycle = 8
    .equ    drate = 1
duty:
    inc    dutycounter
    cpi    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

.exit


;**********
;*  例9-7 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    push    r16
    in        r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r17
    .equ    dcycle = 8
    .equ    drate = 1
duty:
    inc    dutycounter
    cpi    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

.exit


;**********
;*  例9-7 *
;**********
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei
main:
    sleep    
    rjmp    main

timer2vec:
    push    r16
    in        r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r17
    .equ    dcycle = 8
    .equ    drate = 1
duty:
    inc    dutycounter
    cpi    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

.exit
C言語版

#include <avr/sleep.h>
#define USE_TIMER_1     true
#include "TimerInterrupt_Generic.h"

const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
byte counter;
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  counter=0;
  write7seg(0b11011111);
  ITimer1.init();
  ITimer1.attachInterruptInterval(1, vect);
}
void write7seg(byte x){
  for(byte i=0; i<8;i++){
    digitalWrite(out7seg[i],x&1<<i);
  }
}
#define dutymax 16
#define drate 1
byte dutycounter;
void duty(){
  dutycounter++;
  if(dutycounter==dutymax){
    dutycounter=0;
  }
  if(dutycounter<drate){
    digitalWrite(11,HIGH);
  }else{
    digitalWrite(11,LOW);
  }
}

void vect(){
  duty();
}
void loop() {
  sleep_cpu();
}

例9-8

duty 比 1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128 という階調を考えます。 これを RD4 に接続した sw により切替えることを考えます。 但し、これらは計算で求めるのではなく、以前に LED に数字を出した時のよ うにテーブルで参照するようにします。 W レジスタに求める duty 比を入れて、 getduty とすると必要な比 が W レジスタに得られるようにします。 但し、 0 の時、 128 を示し、 1 の時 64, 2 の時 32となる値、つまり最小 が 1 となる相対値を返します。

例9-7同様に 7SEG LED は小数点以外は全て点灯させ、小数点の明るさを変えます。

プログラムは基本的には 例9-6 と演習5-6 のプログラムを流用します。 但し duty 比はプログラムの中で 1 を加え、 duty 比 = (dutyrate - 1)/maxcount ではなく、 duty 比 = dutyrate/maxcount となるようにします。

このプログラムを実現するには、 counter の値により duty_ratio を変化させ る必要があります。 つまり、 sw が押されるたびに、 counter の値を変化させ、その値により duty 比を変化させる必要があります。


;************
;*  例9-8 *
;************
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei


.def    counter=r17
    clr        counter


.def    last = r18
.def    current = r19

main:
    rcall    setduty
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main



timer2vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret
    
    
setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
;.equ    mask = 0b00001000
    lpm    dcycle,Z
    ret



inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,dutymax-1
    ret
startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)

.exit

;************
;*  例9-8 *
;************
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei


.def    counter=r17
    clr        counter


.def    last = r18
.def    current = r19

main:
    rcall    setduty
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main



timer2vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret
    
    
setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
;.equ    mask = 0b00001000
    lpm    dcycle,Z
    ret



inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,dutymax-1
    ret
startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)

.exit

;************
;*  例9-8 *
;************
.cseg
.org 0x0000
    rjmp reset
.org    OVF2addr
    rjmp    timer2vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,high(RAMEND)
    out    SPH,r16
    ldi    r16,low(RAMEND)
    out    SPL,r16

.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

initsleep:
    in    r16,smcr
    cbr    r16,1<<sm0
    cbr    r16,1<<sm1
    cbr    r16,1<<sm2
    sbr    r16,1<<se
    out    smcr,r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

    ldi    r16,0b11110111
    outport    onb,portb
    outport    ond,portd
    sei


.def    counter=r17
    clr        counter


.def    last = r18
.def    current = r19

main:
    rcall    setduty
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main



timer2vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti

    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret
    
    
setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
;.equ    mask = 0b00001000
    lpm    dcycle,Z
    ret



inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,dutymax-1
    ret
startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)

.exit
C言語版

#include <avr/sleep.h>
#define USE_TIMER_1     true
#include "TimerInterrupt_Generic.h"

const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
const byte dutydata[]={1,2,4,8,16,32,64,128,0xff};
const byte button1=4;
const byte button2=5;
byte counter;
byte dcycle;
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  counter=0;
  write7seg(0b11011111);
  ITimer1.init();
  ITimer1.attachInterruptInterval(1, vect);
}
void write7seg(byte x){
  for(byte i=0; i<8;i++){
    digitalWrite(out7seg[i],x&1<<i);
  }
}

#define drate 1
byte dutycounter;
void duty(){
  dutycounter++;
  if(dutycounter==dcycle){
    dutycounter=0;
  }
  if(dutycounter<drate){
    digitalWrite(11,HIGH);
  }else{
    digitalWrite(11,LOW);
  }
}
void vect(){
  duty();
}
void increase(){
  if(dutydata[counter]==0xff){
    counter=0;
  }else{
    counter++;
  }
}
void decrease(){
  if(counter==0){
    counter=sizeof(dutydata)/sizeof(dutydata[0])-1;
  }else{
    counter--;
  }
}
void setduty(){
  dcycle = dutydata[counter];
}
void loop() {
  static bool prev1=HIGH;
  static bool prev2=HIGH;
  static bool now1=HIGH;
  static bool now2=HIGH;
  setduty();
  now1=digitalRead(button1);
  if(now1==HIGH && prev1==LOW){
    decrease();
  }
  prev1=now1;
  now2=digitalRead(button2);
  if(now2==HIGH && prev2==LOW){
    increase();
  }
  prev2=now2;
}

演習9-3

マシン語の例9-8のプログラムで、 PORTB には counter の値を表示するように改造し なさい。

演習9-4

C言語の例9-8のプログラムで、 PORTB には counter の値を表示するように改造し なさい。

9-4. 演習の解説

演習9-1

始めに小数点を約 0.5 秒程度で点滅させることを考えます。 割り込みは Prescaler を 1:256 で使用し、さらに 7 回に一回毎に点滅させ ます。 点滅は点滅パターンを用意して PORTB に対して XOR をかけることで行います。 著者の回路では RB3 が小数点になってますので、 b'00001000' で XOR を行 います。

一方、 RD4 に接続したスイッチを押す度に LED の数字が増えるようにするに は、演習5-6 のプログラムを流用します。 但し、 PORTB にダイレクトに書き込むと小数点の点滅に影響してしまいます。 そこで、ビットパターンを特定の場所だけ上書きすることを考えます。 そのためには、「特定の場所を指定する」情報を考えなくてはいけません。 今、考えるのは PORTB の RA0 を温存したまま RA1 から RA7 までを変更する ことです。

  1. 特定のビットを 1 にする論理演算は or で行います。つまり前のビットが X だった時、 X or 1 = 1 となります。 したがって、パターン中の 1 は or により書き込むことが出来ます。
  2. 特定のビットを 0 にするには and を使います。前のビットが X だった時、 X and 0 = 0 となります。
  3. それでは特定のビットを Y(=0,1) にするにはどうすれば良いでしょうか? 0 と or を取れば Y になりますので、 X と 0 を and をとってから Y と or を取ればよいです。 つまり、 ( X and 0 ) or Y とすることで、 X を Y にすることが出来ます。

つまり、PORTB に pattern を書き込むには、書き込みたい部分を 0 にした マスクと and を取り、それから pattern と or を取れば良いで す。


;************
;*  演習9-1 *
;************
.cseg


.org    0x0000
    rjmp    reset
.org    OVF2addr
    rjmp    timer2vec
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    dispb = 0b00110111
.equ    dispd = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.def    counter=r17
.def    pcounter = r18
.equ    ptime = 20
    ldi    pcounter,ptime
    clr    counter


.def    last = r14
.def    current = r15
    sei
main:
    rcall    dispcount
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec    pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi    pcounter,ptime
    rcall    invertdot
    ret

invertdot:
    push    r17
    push    r20
    in    r17,portb
    ldi    r20,0b00001000
    eor    r17,r20
    out    portb,r17
    pop    r20
    pop    r17
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcount:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcount1
    inc    ZH
dispcount1:
    lpm    r16,Z
    outport    dispb,portb
    outport    dispd,portd
    ret

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,countmax-1
    ret

startdata:
    .db 0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:

.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)
.exit

;************
;*  演習9-1 *
;************
.cseg


.org    0x0000
    rjmp    reset
.org    OVF2addr
    rjmp    timer2vec
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    dispb = 0b00110111
.equ    dispd = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.def    counter=r17
.def    pcounter = r18
.equ    ptime = 20
    ldi    pcounter,ptime
    clr    counter


.def    last = r14
.def    current = r15
    sei
main:
    rcall    dispcount
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec    pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi    pcounter,ptime
    rcall    invertdot
    ret

invertdot:
    push    r17
    push    r20
    in    r17,portb
    ldi    r20,0b00001000
    eor    r17,r20
    out    portb,r17
    pop    r20
    pop    r17
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcount:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcount1
    inc    ZH
dispcount1:
    lpm    r16,Z
    outport    dispb,portb
    outport    dispd,portd
    ret

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,countmax-1
    ret

startdata:
    .db 0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:

.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)
.exit

;************
;*  演習9-1 *
;************
.cseg


.org    0x0000
    rjmp    reset
.org    OVF2addr
    rjmp    timer2vec
.org    INT_VECTORS_SIZE
reset:
    ldi     r16,high(RAMEND)
    out     SPH,r16 
    ldi    r16,low(RAMEND)
    out    SPL,r16
.equ    onb    = 0b00111111
.equ    ond    = 0b11000000
.equ    dispb = 0b00110111
.equ    dispd = 0b11000000
.equ    pullup    = 0b00110000

    ldi    r16,onb
    out    ddrb, r16
    ldi    r16,ond
    out    ddrd, r16
    ldi    r16,pullup
    out    portd, r16

inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000111    ; normal mode, prescaler=1024
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16


.def    counter=r17
.def    pcounter = r18
.equ    ptime = 20
    ldi    pcounter,ptime
    clr    counter


.def    last = r14
.def    current = r15
    sei
main:
    rcall    dispcount
    in    current,PIND

    sbrc    last,PIND4
    rjmp    lpressed
    sbrs    current,PIND4
    rjmp    lnotpressed
lpressednow:
    rcall    inccounter
lpressed:
lnotpressed:

    sbrc    last,PIND5
    rjmp    rpressed
    sbrs    current,PIND5
    rjmp    rnotpressed
rpressednow:
    rcall    deccounter
rpressed:
rnotpressed:

    mov    last,current
    rjmp    main

timer2vec:
    rcall    postscaler
    reti

postscaler:
    dec    pcounter
    breq    postscaler1
    ret
postscaler1:
    ldi    pcounter,ptime
    rcall    invertdot
    ret

invertdot:
    push    r17
    push    r20
    in    r17,portb
    ldi    r20,0b00001000
    eor    r17,r20
    out    portb,r17
    pop    r20
    pop    r17
    ret

.macro    outport
    ldi    r20,@0
    and    r20,r16    
    in    r21,@1
    andi    r21,~@0
    or    r21,r20
    out    @1,r21
.endmacro

dispcount:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcount1
    inc    ZH
dispcount1:
    lpm    r16,Z
    outport    dispb,portb
    outport    dispd,portd
    ret

inccounter:
    inc    counter
    cpi    counter,countmax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret
    
deccounter:
    cpi    counter,0
    breq    deccounter1
    dec    counter
    ret
deccounter1:
    ldi    counter,countmax-1
    ret

startdata:
    .db 0b10110111,0b00100100
    .db 0b01110011,0b01110110
    .db 0b11100100,0b11010110
    .db 0b11010111,0b00110100
    .db 0b11110111,0b11110110
    .db 0b11110101,0b11000111
    .db 0b10010011,0b01100111
    .db 0b11010011,0b11010001
enddata:

.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1
.equ    countmax = low(num_end_adr - num_start_adr)
.exit

演習9-2


#define USE_TIMER_1     true
#include "TimerInterrupt_Generic.h"
const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
const byte pattern[]={
      0b11011110, //0
      0b10010000, //1
      0b11001101, //2
      0b11011001, //3
      0b10010011, //4
      0b01011011, //5
      0b01011111, //6
      0b11010000, //7
      0b11011111, //8
      0b11011011, //9
      0b11010111, //A
      0b00011111, //b
      0b01001110, //C
      0b10011101, //d
      0b01001111, //E
      0b01000111 //F
};
const byte button1=4;
const byte button2=5;
void write();
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  ITimer1.init();
  ITimer1.attachInterruptInterval(500, vect);
  write();
}
void write7seg(byte x){
  for(byte i=0; i<8;i++){
    if(i==5)continue;
    digitalWrite(out7seg[i],(pattern[x]&1<<i)!=0);
  }
}
void vect(){
  blinkdot();
}
void blinkdot(){
  static bool dot=HIGH;
  digitalWrite(out7seg[5],dot^=1);
}
byte p=0;
void increase(){
  if(p>=sizeof(pattern)-1){
    p=0;
  }else{
    p++;
  }
  write();
}
void decrease(){
  if(p==0){
    p=sizeof(pattern)-1;
  }else{
    p--;
  }
  write();
}
void write(){
  write7seg(p);
}
void loop() {
  static bool prev1=HIGH;
  static bool prev2=HIGH;
  static bool now1=HIGH;
  static bool now2=HIGH;
  now1=digitalRead(button1);
  if(now1==HIGH && prev1==LOW){
    decrease();
  }
  prev1=now1;
  now2=digitalRead(button2);
  if(now2==HIGH && prev2==LOW){
    increase();
  }
  prev2=now2;
}    

演習9-3

例9-8のプログラムに、従来のプログラム中にある inccount, dispcount を 組み込むだけです。


;************
;*  演習9-3 *
;************

.cseg
.org 0x0000
    rjmp reset
.org    OVF0addr
    rjmp    timer0vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,low(RAMEND)
    out    SPL,r16
    ldi    r16,0xff
    out    ddrb, r16
initsleep:
    in    r16, smcr
    cbr    r16, 1<<sm0 
    cbr    r16, 1<<sm1
    cbr    r16, 1<<sm2
    sbr    r16, 1<<se
    out    smcr, r16
inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    pattern = r16

    ldi    pattern,0b11110111    ; pattern = 8
    out portb,pattern
.def    counter = r17
.def current = r18
.def    last = r19
     clr    counter    
    sei
main:
    rcall    setduty
    rcall    dispcounter

    in    current,PIND
    sbrc    last,PIND4
    rjmp    pressed
    sbrs    current,PIND4
    rjmp    notpressed
pressednow:
    rcall    inccounter
pressed:
notpressed:
    mov    last,current
    rjmp    main


timer0vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti
    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
    lpm    dcycle,Z
    ret

inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)



.def work = r22
dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
.equ    mask = 0b00001000
    cli
    in    work,portb
    andi    work,mask
    lpm    pattern,Z
    or    work,pattern
    out    portb,work
    sei
    ret


startdata:
    .db    0b01110111,    0b00010100    ;0,1
    .db    0b10110011,    0b10110110    ;2,3
    .db    0b11010100,    0b11100110    ;4,5
    .db    0b11100111,    0b00110100    ;6,7
    .db    0b11110111,    0b11110110    ;8,9
    .db    0b11110101,    0b11000111    ;A,b
    .db    0b01100011,    0b10010111    ;C,d
    .db    0b11100011,    0b11100001    ;E,F
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1


.exit

;************
;*  演習9-3 *
;************

.cseg
.org 0x0000
    rjmp reset
.org    OVF0addr
    rjmp    timer0vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,low(RAMEND)
    out    SPL,r16
    ldi    r16,0xff
    out    ddrb, r16
initsleep:
    in    r16, smcr
    cbr    r16, 1<<sm0 
    cbr    r16, 1<<sm1
    cbr    r16, 1<<sm2
    sbr    r16, 1<<se
    out    smcr, r16
inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    pattern = r16

    ldi    pattern,0b11110111    ; pattern = 8
    out portb,pattern
.def    counter = r17
.def current = r18
.def    last = r19
     clr    counter    
    sei
main:
    rcall    setduty
    rcall    dispcounter

    in    current,PIND
    sbrc    last,PIND4
    rjmp    pressed
    sbrs    current,PIND4
    rjmp    notpressed
pressednow:
    rcall    inccounter
pressed:
notpressed:
    mov    last,current
    rjmp    main


timer0vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti
    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
    lpm    dcycle,Z
    ret

inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)



.def work = r22
dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
.equ    mask = 0b00001000
    cli
    in    work,portb
    andi    work,mask
    lpm    pattern,Z
    or    work,pattern
    out    portb,work
    sei
    ret


startdata:
    .db    0b01110111,    0b00010100    ;0,1
    .db    0b10110011,    0b10110110    ;2,3
    .db    0b11010100,    0b11100110    ;4,5
    .db    0b11100111,    0b00110100    ;6,7
    .db    0b11110111,    0b11110110    ;8,9
    .db    0b11110101,    0b11000111    ;A,b
    .db    0b01100011,    0b10010111    ;C,d
    .db    0b11100011,    0b11100001    ;E,F
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1


.exit

;************
;*  演習9-3 *
;************

.cseg
.org 0x0000
    rjmp reset
.org    OVF0addr
    rjmp    timer0vec
.org INT_VECTORS_SIZE
reset:
    ldi    r16,low(RAMEND)
    out    SPL,r16
    ldi    r16,0xff
    out    ddrb, r16
initsleep:
    in    r16, smcr
    cbr    r16, 1<<sm0 
    cbr    r16, 1<<sm1
    cbr    r16, 1<<sm2
    sbr    r16, 1<<se
    out    smcr, r16
inittimer2:
    ldi    r16,0b00000000    ;normal mode
    sts    tccr2a,r16
    ldi    r16,0b00000001    ; normal mode, prescaler=1
    sts    tccr2b, r16
    ldi    r16, 0b00000001 ; enable interruption for overflow
    sts    timsk2, r16

.def    pattern = r16

    ldi    pattern,0b11110111    ; pattern = 8
    out portb,pattern
.def    counter = r17
.def current = r18
.def    last = r19
     clr    counter    
    sei
main:
    rcall    setduty
    rcall    dispcounter

    in    current,PIND
    sbrc    last,PIND4
    rjmp    pressed
    sbrs    current,PIND4
    rjmp    notpressed
pressednow:
    rcall    inccounter
pressed:
notpressed:
    mov    last,current
    rjmp    main


timer0vec:
    push    r16
    in    r16,SREG
    push    r16
    rcall    duty
    pop    r16
    out    SREG,r16
    pop    r16
    reti
    .def    dutycounter = r20
    .def    dcycle = r21
    .equ    drate = 1
duty:
    inc    dutycounter
    cp    dutycounter,dcycle
    brcs    duty1
    clr    dutycounter
duty1:
    cpi    dutycounter,drate
    brcs    onstate
    rjmp    offstate
onstate:
    sbi    portb,3
    ret
offstate:
    cbi    portb,3
    ret

setduty:    ;counterの値に対応するduty比を設定
    ldi    ZH,high(startdutydata<<1)
    ldi    ZL,low(startdutydata<<1)
    add    ZL,counter
    brcc    setduty1
    inc    ZH
setduty1:
    lpm    dcycle,Z
    ret

inccounter:
    inc    counter
    cpi    counter,dutymax
    breq    inccounter1
    ret
inccounter1:
    clr    counter
    ret

startdutydata:
    .db    1,2,4,8,16,32,64,128
enddutydata:
.equ    duty_start_adr = startdutydata <<1
.equ    duty_end_adr = enddutydata << 1
.equ    dutymax = low(duty_end_adr - duty_start_adr)



.def work = r22
dispcounter:    ;counter    の値を表示
    ldi    ZH,high(startdata<<1)
    ldi    ZL,low(startdata<<1)
    add    ZL,counter
    brcc    dispcounter1
    inc    ZH
dispcounter1:
.equ    mask = 0b00001000
    cli
    in    work,portb
    andi    work,mask
    lpm    pattern,Z
    or    work,pattern
    out    portb,work
    sei
    ret


startdata:
    .db    0b01110111,    0b00010100    ;0,1
    .db    0b10110011,    0b10110110    ;2,3
    .db    0b11010100,    0b11100110    ;4,5
    .db    0b11100111,    0b00110100    ;6,7
    .db    0b11110111,    0b11110110    ;8,9
    .db    0b11110101,    0b11000111    ;A,b
    .db    0b01100011,    0b10010111    ;C,d
    .db    0b11100011,    0b11100001    ;E,F
enddata:
.equ    num_start_adr = startdata <<1
.equ    num_end_adr = enddata << 1


.exit

演習9-4


#include <avr/sleep.h>
#define USE_TIMER_1     true
#include "TimerInterrupt_Generic.h"
const byte pattern[]={
            0b11011110, //0
            0b10010000, //1
            0b11001101, //2
            0b11011001, //3
            0b10010011, //4
            0b01011011, //5
            0b01011111, //6
            0b11010000, //7
            0b11011111, //8
            0b11011011, //9
            0b11010111, //A
            0b00011111, //b
            0b01001110, //C
            0b10011101, //d
            0b01001111, //E
            0b01000111 //F
};
const byte out7seg[]={6,7,8,9,10,11,12,13,0xff};
const byte dutydata[]={1,2,4,8,16,32,64,128,0xff};
const byte button1=4;
const byte button2=5;
byte counter;
byte dcycle;
void write();
void setup() {
  for(byte i=0;out7seg[i]!=0xff;i++){
    pinMode(out7seg[i], OUTPUT);
  }
  pinMode(button1,INPUT_PULLUP);
  pinMode(button2,INPUT_PULLUP);
  counter=0;
  ITimer1.init();
  ITimer1.attachInterruptInterval(1, vect);
  write();
}


#define drate 1
byte dutycounter;
void duty(){
  dutycounter++;
  if(dutycounter==dcycle){
    dutycounter=0;
  }
  if(dutycounter<drate){
    digitalWrite(11,HIGH);
  }else{
    digitalWrite(11,LOW);
  }
}

void vect(){
  duty();
}


void increase(){
  if(dutydata[counter]==0xff){
    counter=0;
  }else{
    counter++;
  }
  write();
}
void decrease(){
  if(counter==0){
    counter=sizeof(dutydata)/sizeof(dutydata[0])-1;
  }else{
    counter--;
  }
  write();
}
void write(){
  write7seg(counter);
}
void write7seg(byte x){
  for(byte i=0; i<8;i++){
    if(i==5)continue;
    digitalWrite(out7seg[i],(pattern[x]&1<<i)!=0);
  }
}

void setduty(){
  dcycle = dutydata[counter];
}
void loop() {
  static bool prev1=HIGH;
  static bool prev2=HIGH;
  static bool now1=HIGH;
  static bool now2=HIGH;
  setduty();
  now1=digitalRead(button1);
  if(now1==HIGH && prev1==LOW){
    decrease();
  }
  prev1=now1;
  now2=digitalRead(button2);
  if(now2==HIGH && prev2==LOW){
    increase();
  }
  prev2=now2;
}    

坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科