AVRでアセンブラーの練習 2007.09.03(月)〜 / LessonA03。 タイマ割込みで10進カウント。LEDを1秒ごとに点滅。
著作者名: 中野 良知 作成開始: 2007.09.02(日) 更新 : 2009.08.02(日) △ 誤記訂正 intr_time → intr_time0。
目次 1. 目的 2. フローチャート 3. プログラムソースコード 4. キーワード 4.1. 割込みベクター設定 4.2. スタック 4.3. ステータスの退避と復帰 4.4. タイマー0の割込み周期 4.5. 10進カウンタ 4.6. 引数が2つのマクロ 5. 動作確認 6. 実験回路 1. 目的 タイマー0のオーバーフロー割込みで、10進4桁のカウントを実施。 同10進カウンターの1秒桁の値で、PORTB bit0のLEDを点滅させます。 2. フローチャート (main) ↓ ポートの初期化 アナログ比較器の電源OFF ウォッチドック動作禁止 タイマー0初期化。1msec毎にオーバーフロー割込み ↓ ├────────────┐ ↓ │ accに1秒カウンタ値を取得 │ ↓ │ accのbit0以外をマスク │ ↓ │ ポートBへacc値を出力 │ ↓ │ └────────────┘ (タイマー0割込み) ↓ ステータスレジスター退避 ↓ acc退避 ↓ 1msecから1sの10進カウンター更新 ↓ acc復帰 ↓ ステータスレジスター復帰 ↓ (終了) 3. プログラムソースコード シフト記号"<<"は半角記号に変更してください。 ;============================================================= ; タイトル : LessonA03 ; 著者 : 中野 良知 ; 作成開始 : 2007.09.02(日) 作成開始 ; 最新修正 : 2007.09.02(日) ; 動作クロック : CLOCK=1MHz。内蔵RC発振器を使用 ;============================================================= ; インクルード .include "tn2313def.inc" ; ;============================================================= ; 定数定義 ;============================================================= ; タイマカウント .equ T1MSEC = 256 - 125 ; CLOCK=1MHz、Pre Scale=1/8、1msec .equ PRE_SCALE = $02 ; 1/8。タイマスタート .equ TINIT = 256 - 10 ; ;============================================================= ; 汎用レジスターに名前を定義(変数定義) .def sreg_save = R0 ; SREG保存用 .def acc = R16 ; アキュムレーター .def t1ms = R17 ; 1msecタイマカウンタ .def t10ms = R18 ; 10msecタイマカウンタ .def t100ms = R19 ; 100msecタイマカウンタ .def t1s = R20 ; 1secタイマカウンタ ;============================================================= ; マクロ定義 ;============================================================= .macro TIME_COUNT ; TIME_COUNT @0 @1 inc @0 ; 引数@0の汎用レジスタインクリメント cpi @0,10 ; 同汎用レジスタ=10? brne @1 ; 10でなければ引数@1へジャンプ clr @0 ; 同汎用レジスタを初期化 .endmacro ;============================================================= ; SRAM定義 ;============================================================= ; スタック .equ STACK_INIT = $00DF ; スタック初期値 .dseg ; データセグメント=SRAM .org STACK_INIT ; stack_top: .byte 1 ; 昇順で使用。ダミーの1バイトを確保 ;============================================================= ; プログラム ;============================================================= .cseg ; CODE SEGMENT ;============================================================= ; リセットスタートベクター ;============================================================= .org 0 ; リセットスタートベクタ start: rjmp main ; mainへ ;============================================================= ; タイマ0オーバーフロー割込みベクター ;============================================================= .org 6 ; タイマ/カウンタ0オバーフロベクタ rjmp intr_time0 ; intr_time0へ ;============================================================= ; メイン ;============================================================= main: // スタックポインター初期化 ldi acc,low(STACK_INIT) ; out spl,acc ; スタックポインター下位設定 // PORTB 初期化 ldi acc,$00 ; 出力データ out PORTB,acc ; PORTBの全ビットをLowに設定 ldi acc,$FF ; PORTB全ビット出力 out DDRB,acc ; PORTB入出力方向設定 // アナログ比較器初期化 sbi ACSR,ACD ; アナログ比較器電源オフ // ウォッチドッグ(WD)禁止 wdr ; WDタイマリセット in acc,MCUSR ; MCUSR値を取得 andi acc,~(1<<WDRF) ; WDRF=0 out MCUSR,acc ; WD RESETフラグ(WDRF)解除 in acc,WDTCSR ; 現WDTCSR値を取得 ori acc,(1<<WDCE)|(1<<WDE) ; WDCE=WDE=1を設定 out WDTCSR,acc ; WDCEとWDEに書き込み ldi acc,$00 ; WDCE=WDE=0を設定 out WDTCSR,acc ; WD禁止 // Timer/Counter 0 初期化 // TCCR0A=0x00 ; 標準動作 in acc,TIMSK ; タイマ割込みマスクレジスタ取得 sbr acc,(1<<TOIE0) ; オーバーフロー割込み許可ビットセット out TIMSK,acc ; タイマ0 オーバーフロー割込み許可 ldi acc,T1MSEC ; 1msecカウント値 out TCNT0,acc ; タイマ0カウンタ設定 ldi acc,PRE_SCALE ; プリスケール値。1/8。8usec。 out TCCR0B,acc ; タイマ0スタート。プリスケール設定 clr t1ms ; 1msecカウンタ初期化 clr t10ms ; 10msecカウンタ初期化 clr t100ms ; 100msecカウンタ初期化 clr t1s ; 1secカウンタ初期化 sei ; 全割込み許可 led_on_off: mov acc,t1s ; 1secカウンタ値をコピー andi acc,$01 ; LSB以外をマスク out PORTB,acc ; 8ビット出力。bit0がオン・オフ rjmp led_on_off ; ループ ;============================================================= ; タイマ0割込み ;============================================================= intr_time0: in sreg_save,SREG ; ステータスを退避 push acc ; acc退避 ldi acc,T1MSEC ; 1msecカウント値 out TCNT0,acc ; タイマ0カウンタ設定 TIME_COUNT t1ms,intr_time0_end ; 1msecカウント TIME_COUNT t10ms,intr_time0_end ; 10msecの桁上がりカウント TIME_COUNT t100ms,intr_time0_end ; 100msecの桁上がりカウント TIME_COUNT t1s,intr_time0_end ; 1sの桁上がりカウント intr_time0_end: pop acc ; acc復帰 out SREG,sreg_save ; ステータスを復帰 reti ; ;============================================================= ;============================================================= ; END ;============================================================= 4. キーワード 4.1. 割込みベクター設定 ATtiny2313の割込みベクターテーブルは ┌─────┬────────────┐ │ アドレス │ 内容 │ ├─────┼────────────┤ │ $0000 │リセットスタート │ ├─────┼────────────┤ │ $0001 │INT0 │ ├─────┼────────────┤ 〜 〜 〜 ├─────┼────────────┤ │ $0006 │タイマー0オーバーフロー │ ├─────┼────────────┤ 〜 〜 〜 └─────┴────────────┘ です。一部分のみを抜粋。 *デバイスによってベクターの配置が異なります。 .cseg でコードセグメント領域を指定します。 .org 0 でアドレス$0000番地を指定します。 rjmp main ベクターテーブルを飛び越えて、mainへジャンプします。 .org 6 でアドレス$0006番地を指定します。 rjmp intr_time0 で割込みルーチンへジャンプします。 4.2. スタック ATtiny2313のデータRAM構成です。 ┌─────┬────────────┐ │ アドレス │ 内容 │ ├─────┼────────────┤ │$00〜$1F │汎用レジスター │ ├─────┼────────────┤ │$10〜$5F │I/Oレジスター │ ├─────┼────────────┤ │$60〜$DF │SRAM │ └─────┴────────────┘ SRAMをスタック領域として使用します。 スタックには、割込み発生時やサブルーチンをコールした時のプログラム・カウンタ の値が自動的に格納されます。 格納先はスタックポインター(spl)によって指定され、格納後にsplは-2されます。 splには初期値として$DFを設定します。 ┌───┐ $60 │ │ 〜 〜 $DD │ │ $DE │ │ $DE │ │ $DF │ │← spl が指定するアドレス └───┘ タイマー0の割込みが発生すると、 1) 実行中の命令の終了待ち 2) プログラムカウンター(PC)を次の命令のアドレスに更新 3) PCの値をスタックへ退避 4) splを-2 の後に、割込みベクターへジャンプします。更に割込みベクターからintr_timeへ ジャンプします。 ┌───┐ $60 │ │ 〜 〜 $DD │ │ $DE │ │← spl-2された後の位置 $DE │PC上位│ $DF │PC下位│ └───┘ また、汎用レジスターの値もスタックに格納できます。 push acc でacc(R16)の値がsplが示すスタックへ格納されます。 格納後splは-1されます。 ┌───┐ $60 │ │ 〜 〜 $DD │ │← spl-1された後の位置 $DE │acc値 │ $DE │PC上位│ $DF │PC下位│ └───┘ pop acc でははじめにsplが+1され、splが示すスタックからデータを acc(R16)へ転送します。スタックにはaccが残ります。 ┌───┐ $60 │ │ 〜 〜 $DD │ │ $DE │acc値 │← spl+1された後の位置。スタックのデータをaccへ転送します。 $DE │PC上位│ $DF │PC下位│ └───┘ 4.3. ステータスの退避と復帰 割込みルーチンでステータスが変化したまま通常ルーチンへ復帰すると、通常ルーチ ンで不具合が発生することがあります。 これを避けるためにタイマー割り込みルー チンのはじめで、ステータスレジスター(SREG)を退避します。 △1 誤記訂正 mov→in in sreg_save,SREG ここでは、sreg_save(R0)へSREGの値を保存しました。 割込みルーチンから戻る直前に、SREGの値を復帰します。 △2 誤記訂正 mov→out out SREG,sreg_save この他に、SREGをスタックへ退避する方法もあります。 push acc ; はじめにacc(R16)を退避します。 mov acc,SREG ; accを経由して push acc ; SREGをスタックへ退避します。 ┌───┐ $60 │ │ 〜 〜 $DC │ │← acc経由でSREG値をpushした後に、splが指定するアドレス $DD │SREG値│ $DE │acc値 │ $DE │PC上位│ $DF │PC下位│ └───┘ 割込みルーチンから戻る直前に、pushとは逆順にSREGとaccの値を復帰します。 pop acc ; スタックからSREG値をaccへ復帰 mov SREG,acc ; acc経由でSREGを復帰 pop acc ; accを復帰 reti ; 割込みルーチンを終了 割込みルーチンから戻ると、splはスタックのはじめの位置に戻ります。 ┌───┐ $60 │ │ 〜 〜 $DC │ │ $DD │SREG値│ $DE │acc値 │ $DE │PC上位│ $DF │PC下位│← spl が指定するアドレス └───┘ 4.4. タイマー0の割込み周期 1MHzのクロックを1/8に分周します。 ldi acc,PRE_SCALE ; プリスケール値。1/8。8usec。 out TCCR0B,acc ; タイマ0スタート。プリスケール設定 タイマー0のカウントクロックの周期は8usecです。 タイマー0カウンターが125をカウントしてオーバーフローになります。 ldi acc,T1MSEC ; 1msecカウント値 out TCNT0,acc ; タイマ0カウンタ設定 TCN0=256-125=131 の設定で、タイマー0の割込み周期は8usec*125=1msecです。 4.5. 10進カウンタ 1msecごとのタイマー0の割込みルーチンで、10進4桁のカウンターをインクリメント します。 1msecカウンタを+1 ↓ no <10msec?> ───────┐ ↓ │ 1msecカウンタ=0 │ ↓ │ 10msecカウンタを+1 │ ↓ no │ <100msec?>───────┤ ↓ │ 10msecカウンタ=0 │ ↓ │ 100msecカウンタを+1 │ ↓ no │ <1000msec?> ──────┤ ↓ │ 100msecカウンタ=0 │ ↓ │ 1secカウンタを+1 │ ↓ no │ <1sec?> ────────┤ ↓ │ 1secカウンタ=0 │ ↓ │ │ │ ├──────────┘ ↓ 1secカウンタ(t1s)は1秒ごとにカウントアップし、10になると直ちに0に初期化する ため、mainルーチンから見たt1sは次の様に変化します。 $00→$01→$02→$03→...→$08→$09→$00→$01→.... これを mov acc,t1s ; 1secカウンタ値をコピー andi acc,$01 ; LSB以外をマスク でt1sのbit0以外をマスクすると、bit0(LSB)が1秒ごとに0と1を繰返します。 $00→$01→$00→$01→...→$00→$01→$00→$01→.... この値をPORTBへ出力すると、bit0に接続したLEDが1秒ごとに点滅します。 4.6. 引数が2つのマクロ .macro TIME_COUNT ; TIME_COUNT @0 @1 inc @0 ; 引数@0の汎用レジスタインクリメント cpi @0,10 ; 同汎用レジスタ=10? brne @1 ; 10でなければ引数@1へジャンプ clr @0 ; 同汎用レジスタを初期化 .endmacro 用例: TIME_COUNT t1ms,intr_time0_end ; 1msecカウント は inc t1ms ; 引数@0の汎用レジスタインクリメント cpi t1ms,10 ; 同汎用レジスタ=10? brne intr_time0_end ; 10でなければ引数@1へジャンプ clr t1ms ; 同汎用レジスタを初期化 に展開、更に inc R17 ; 引数@0の汎用レジスタインクリメント cpi R17,10 ; 同汎用レジスタ=10? brne intr_time0_end ; 10でなければ引数@1へジャンプ clr R17 ; 同汎用レジスタを初期化 に変換されてアセンブルされます。 プログラムでは、マクロを連続して使用します。 TIME_COUNT t1ms,intr_time0_end ; 1msecカウント TIME_COUNT t10ms,intr_time0_end ; 10msecの桁上がりカウント TIME_COUNT t100ms,intr_time0_end ; 100msecの桁上がりカウント TIME_COUNT t1s,intr_time0_end ; 1sの桁上がりカウント 5. 動作確認 0.98sec 0.98sec │←──→│←──→│ ┌────┐ ┌────┐ RB0 ──┘ └────┘ └─── 偏差 = -2% 6. 実験回路 ATtiny2313 ┌────┐20 │ VCC├────●──────────●────●──── +5V │ │ R1 │ │ │ │ │ R10K C1 │ │ │ │1 │ 0.1u/50V │ │ │ -RESET├────●───┤├──┐ │ C2 │ │ │ │ │ 0.1u │ C3 │ │ D1 │ ┴ /50V │┴│100u/16V │ │12 ┌─┬┐ R2 │ ┬ └┬┘ │ PB0├──┤>│├─R220─● │ │ │ │ └─┴┘ │ │ │ │ │10 LED │ │ │ │ GND├────────────●──●────●──── 0V(GND) │ │ └────┘ C2(0.1u/50V)は、VCCとGNDの近くに接続します。
LessonAのTopへ
サイトのTopへ
法律条項 この資料により生じたいかなる障害や損害に対し、著者は全てを免責されるものとします。 この資料は、著作権法の下で保護され、入手先、著者、日付、法律条項を含んだ場合にのみ複製が可能です。
inserted by FC2 system