● このページについて ■先頭に戻る▲1つ戻る |
このページは、マイクロチップ社の PICマイコンでよく使うテクニックなどをまとめたものです。
掲載しているコードはささおの許可なく使用してかまいませんし、
各項目への直リンクも自由におこなってかまいません(URLは変更しません)が、
無保証ってことでよろしくお願いします。
ご意見・バグ報告・ネタ提供などありましたら、メールやTwitterでどうぞ。おまちしています。 対象PIC: ミッドレンジのPIC (PIC16F84, PIC16F876, PIC16F877, PIC16F628など) アセンブラ: MPASM 02.30.11 (MPLABに同梱。マイクロチップ社のページから無償ダウンロードできます。) |
● ビット間のコピー ■先頭に戻る▲1つ戻る | |
; ; _DATA1 の bit 3 を _DATA2 の bit 7 にコピー ; btfss _DATA1,3 bcf _DATA2,7 btfsc _DATA1,3 bsf _DATA2,7 |
PICでは、RAMの総容量が数十〜400バイト程度しかないので、
RAM の1バイトは血の1滴です。true/false しか必要ないフラグ等は、
1バイトに8個ずつまとめてしまいましょう。 こんなときによく使うビット間のコピーは左のような感じで実装します。 |
● 大小比較(符号無し8bit) ■先頭に戻る▲1つ戻る | |
;[ if _DATA1 = _DATA2 then goto Xxx ] movf _DATA2,W xorwf _DATA1,W btfsc STATUS,Z goto Xxx |
PICは、〜の条件のときXxxへジャンプのような命令が存在しませんので、
既存の命令を組み合わせて作ります。通常このような場合は引き算命令のフラグ変化を
利用するのがセオリーですが、PICは引き算の方向が通常のCPUとは逆だったり、
条件判定には
xxxフラグが立っていない場合には次の命令を実行しない (btfsc) などという二重否定な命令を使わなければならないので、
頭が痛くなってしまいます。
そこで、よく使うものを左にまとめてみました。
関連事項: ● ゼロチェック, ● and/or/xorのよくある使い方 |
● ゼロチェック ■先頭に戻る▲1つ戻る | |
;[ if _DATA = 0 then goto Xxx ] movf _DATA,F btfsc STATUS,Z goto Xxx |
movf 命令は Zフラグを変化させます。
これを利用することで W レジスタを破壊することなく、
ある変数が0であるかをチェックできます。 16bit 以上の数のゼロチェックをする場合は、すべての桁の OR を取り 結果が 0であることを利用するのが早いです。 関連事項: ● N回ループ, ● and/or/xorのよくある使い方 |
● N回ループ ■先頭に戻る▲1つ戻る | |
; ; (例1) 100 回ループ(カウンタ減少型, 8ビットの場合) ; movlw D'100' ; 0の場合256回ループ movwf _COUNTER Loop ; ループさせる処理をここに書く ; ループ終了処理 decfsz _COUNTER,F goto Loop Next |
N回ループしたいという状況は頻繁に生じます。
decfsz命令を使用するとループのためのコードを節約することができます(例1)。
ただし、_COUNTERは 100, 99, .... , 1 と変化していくので、_COUNTERの
値をつかって何かしたい場合にはご注意。
さらにループ回数を増やしたい場合や、0,1,2,...というように
カウンタを増やしていきたい場合、あんまりややこしいことを考えたくない場合など
(つまり大抵の場合)では (例2) の方法がいいかと思います。
|
● 定数テーブルの参照 ■先頭に戻る▲1つ戻る | |
; ; 定数テーブルの参照 ; _DATA に 0〜7 を入れてこのルーチンを call すると、 ; W に あらかじめ設定しておいた値が入ります ; org 0x100 GetTable movlw H'01' movwf PCLATH ; テーブルは 0x100〜0x1FF番地内に存在 movf _DATA,W andlw B'00000111' ; _DATA の下位 3bitのみを参照 addwf PCL,F ; PCL ← (PCL + W) & 0xFF ; PCH ← PCLATH ; の2つの動作が実行されます ; テーブル retlw B'00000001' ; _DATA = 0 のとき retlw B'00000011' ; _DATA = 1 retlw B'00000111' ; _DATA = 2 retlw B'00001111' ; _DATA = 3 retlw B'00011111' ; _DATA = 4 retlw B'00111111' ; _DATA = 5 retlw B'01111111' ; _DATA = 6 retlw B'11111111' ; _DATA = 7 |
定数テーブルを作るときは PC(プログラムカウンタ)を操作して、
retlw 命令と組み合わせて使うのが早いです。
ただし、PCを操作する上で、次の点に注意してください。
1.あらかじめ決めたテーブルの範囲を超えないようにすること
2. retlw の配置されるアドレスを意識すること |
● mpasm の 日本語DOS窓使用 ■先頭に戻る▲1つ戻る | |
@echo off c:\progra~1\mplab\mpasm /w1 /q %1.asm type %1.err |
日本語 DOS窓環境で mpasm (microchip純正アセンブラ)を使用するためには、
左の内容をテキストエディタで作成し、mpasm.bat というファイル名で保存後、
パスの通ったディレクトリに配置してください。
mpasm ファイル名 (拡張子は不要) でアセンブルできます。 |
● 配列変数(間接アドレッシング)の実装 ■先頭に戻る▲1つ戻る | |
1. メモリの確保 _DATA res D'10' ; 10バイト分確保 _POS res 1 ; _DATAの先頭アドレスからの相対位置 _BUF res 1 ; データの読み書き用 |
RAMの少ないPICでも、配列変数くらいは使いたいものです。
PICでは、
1. FSR レジスタに読み書きしたいRAMのアドレスを設定する。
配列変数は、これらのレジスタを使って左の例のように実装することができます。 |
● and/or/xor のよくある使い方 ■先頭に戻る▲1つ戻る | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
and の よくある使い方 ・ビットマスク 11010101 and) 00011110 ← 1とした部分だけ 00010100 ← 値が素通り。残りは0。 ・2のべき乗 (2, 4, 8, ...)での割り算の余りの計算 A ÷ 8 の 余り ≡ A and 7(= B'00000111') A ÷ 64 の 余り ≡ A and 63(= B'00111111') などなど |
真理表(↑)だけ眺めていても、いまいちピンとこない論理演算。 よくある使い方をまとめてみました。
and
or
xor |
● 変数の値によるプログラムの分岐 ■先頭に戻る▲1つ戻る | |
; ; 変数の値によるプログラムの分岐 ; _DATA に 0〜4 を入れてこのルーチンに goto すると、 ; _DATA の値に応じたプログラムへジャンプします ; 範囲外の値の場合には Default にジャンプします ; org 0x100 OnGoto movf _DATA,W sublw D'4' btfss STATUS,C goto Default ; if 4 < _DATA goto Default movlw H'01' movwf PCLATH ; テーブルは 0x100〜0x1FF番地内に存在 movf _DATA,W addwf PCL,F ; PCL ← (PCL + W) & 0xFF ; PCH ← PCLATH ; の2つの動作が実行されます ; テーブル goto Routine0 ; _DATA = 0のとき goto Routine1 ; _DATA = 1のとき goto Routine2 ; _DATA = 2のとき goto Routine3 ; _DATA = 3のとき goto Routine4 ; _DATA = 4のとき |
変数の値に応じてプログラムを分岐したい場合 (on xxx goto ..) には、
PC(プログラムカウンタ)を操作するのが簡単です。但し、
すべてのテーブルジャンプ先(goto Routine...)が必ず PCLATHで指定した範囲に収まっているかを .lstファイルで確認してください。
PC の操作については、下記の関連事項に目を通しておくことをお勧めします。
PCの動作を理解していないと「いままで普通に動いてたのにプログラムが
大きくなってきたらときどき変な動作をするようになった」
というワナに陥りがちです。
関連事項: ● PIC におけるプログラムカウンタ(PC)の操作 |
● PIC におけるプログラムカウンタ(PC)の操作 ■先頭に戻る▲1つ戻る | |
; ; 0x800 の壁を考慮したプログラム ; org 0x100 Sub1 movf PCLATH,W movwf _PCLATH_BUF ; PCLATHを退避 movlw H'08' movwf PCLATH ; call を実行すると(現在のPC+1)が ; 保存された後 call Sub2 ; PC<12:11>←PCLATH<4:3> ; PC<10: 0>←Sub2の下位11ビット ; がセットされます movf _PCLATH_BUF,W movwf PCLATH ; PCLATHを復帰 return org 0x800 Sub2 : : return |
プログラムの先読みや同時に複数の命令を実行するような
最近の超高速なCPUでは、プログラムが現在どの部分を実行しているかを
つかさどるプログラムカウンタ(PC)をユーザが直接書き換える
ということはご法度ですが、
PICでは PC を直接操作することを前提にした命令セットになっています。
PICでは、13 bit のメモリ空間 ( 0x0000 〜 0x1FFF ) がありますが、
扱いはやや複雑です。
PC, PCH, PCL, PCLATH
0x0800 の壁 |
● 2のN乗の乗除算 ■先頭に戻る▲1つ戻る | |
; _A(8bit) を 4=2*2 倍する bcf STATUS,C rlf _A,F ; _A = _A*2 bcf STATUS,C rlf _A,F ; _A = _A*2 |
十進数で10倍,100倍,... あるいは、1/10倍, 1/100倍の計算が簡単なように、
二進数では 2倍, 4倍, 8倍,... あるいは 1/2倍, 1/4倍,... の計算が簡単にできます。 十進数で桁を一桁上げることは、二進数では1ビット分左にシフト ( STATUS,C をリセットして左回転 )、 十進数で一桁下げることは、二進数では1ビット分右にシフト ( STATUS,C をリセットして右回転 ) にそれぞれ対応します。
2倍 1/2倍 (二進数) 右シフト 11010011 (= 211) → 01101001.1 (=105.5) ↓ 左シフト ↓ 110100110 (= 422) 小数点部は ↓ STATUS,Cに 8bitからあふれた分は 反映される STATUS,C に反映される16bit以上の数値を扱いたい場合には、乗算の場合は下位から、除算の場合は上位の桁から計算していきます。十進数の乗除算と同じですね。 |
● 符号無し乗算(8ビットx8ビット=16ビット) ■先頭に戻る▲1つ戻る | |
; ; RAMの割り当て ; _H res 1 _L res 1 _A res 1 _B res 1 |
PICには乗算命令がありません(AVRにはあります)。
よって、PICでは乗算は極力使わないようにするか、
2のN乗の乗算を組み合わせる
(たとえば10倍したければ、8倍と2倍を求めて和をとる)
等の工夫をして高速化します。
でも、それでもどうしても一般的な乗算がやりたい場合は
左のようなプログラムで計算できます。
ちなみに計算終了まで 80命令程度の時間が必要です。
関連事項: ● 符号付き乗算(8ビットx8ビット=16ビット) |
● 符号付き乗算(8ビットx8ビット=16ビット) ■先頭に戻る▲1つ戻る | |
; ; RAMの割り当て ; _H res 1 _L res 1 _A res 1 _B res 1 |
PICには乗算命令がないどころか符号付き演算用命令がありませんので、
符号付きの数同士の演算はBASICやC言語においてもやらないほうがいいです。
符号無しの計算でうまく処理できないかどうか考えましょう。
でも、どうしても必要というのなら左のような方法で符号付の2数の
乗算をすることができます。左の例では Boothのアルゴリズムを用いています。
ちなみに計算終了まで 120命令程度の時間が必要です。
関連事項: ● 符号無し乗算(8ビットx8ビット=16ビット) |
● 16ビットの加減算 ■先頭に戻る▲1つ戻る | |
; ; RAMの割り当て ; _AH res 1 ; _AH,_AL と _BH, _BL _AL res 1 ; をそれぞれ 16 bit レジスタ _BH res 1 ; _A, _B とみなして計算 _BL res 1 |
8ビットの加減算命令しかないPICで16ビットの加減算をする時には
キャリ/ボロービット (STATUS,C) を利用します(赤字部分)。 このビットは、計算結果が 8bit に収まらないような繰り上がり、 桁借りの演算で次のように変化します。 addwf reg,F の演算の例 (1) 繰り上がり無し (2) あり H'23' ← W H'F0' ← W +) H'56' ← reg +) H'25' ← reg H'79' ← reg H'15' ← reg STATUS,C = 0 STATUS,C = 1 subwf reg,F の演算の例 (1) 桁借り無し (2) あり H'83' ← reg H'11' ← reg -) H'56' ← W -) H'30' ← W H'2D' ← reg H'E1' ← reg STATUS,C = 1 STATUS,C = 0結果の格納先が F ではなく W の時や、addlw, sublw 命令の場合にもSTATUS,C は同様の変化をします。 |
● 擬似乱数の発生 ■先頭に戻る▲1つ戻る | |
; ; 擬似乱数の発生 ; ; (1) RAMの割り当て _RNDH res 1 ; _RND:乱数用 _RNDL res 1 ; (16bit) _BH res 1 ; _B:中間処理用 _BL res 1 ; (16bit) |
'本当の'乱数を作るのは大変ですが、ほぼランダム、というのなら
元の数をxxx倍してxxを加えてごにょごにょという感じで作ることができます。
左の例では、61124回の周期をもち、_RNDLは 0〜255の範囲をほぼ偏りなくばらつきます。 ただし、この場合では電源を入れるたびに毎回同じパターンが出現しますので。 それがイヤな場合には、_RNDLの値に対して、I/OピンやRTCの値と適当なタイミングで XOR をとったりするといいでしょう。 関連事項: ● 16ビットの加減算, ● 2のN乗の乗除算 |
管理人 ささお