第17週 16F877で遊ぶ(まだ準備体操3)
規制緩和の大波に全社一丸となって立ち向かっている、とある会社。
なにより全社員の意識改革が必要と、まずは社員の提案活動から。手始めに、まだ会社の古い体質に染まっていない新米女子社員に経費節減について意見を述べてもらいました。
「副社長ってぇ、2人もいらないんじゃないですかぁ」
その通り! 真摯に会社の発展を考える君たちのような社員がいる限り、この会社の前途は洋々だ!
副社長が納得すればの話ですけど。(^^;
あ、言っときますけど、私とはぜぇんぜん関係ないですからね、この話。また聞きのまた聞きのそのまた聞きですからね。(^^;
16F877で遊ぶ(3)サブルーチン作り
先週はうまいことモータが回って表示もできたので、いよいよ新しい世界へ踏み出します。(んな気張るほどのことでもないか。)
A/D変換をやってみます。A/D入力電圧をLCDに表示ってのはどうでしょう。緻密なスケーリングは面倒そうなので「概ねこんな電圧に近いかな」程度の表示ってことで手を打ちます。
一応表示させないことには始まらないですが、それには10進表示が必要で、バイナリ−BCD変換させなければなりません。さらにそのためには引き算しなければなりません。とんでもないことになっちゃったぞ、これは。
引き算サブルーチン、BIN−BCD変換サブルーチンを作ります。
自己流。プロのみなさん、お笑い下さいな。
AD変換が目的なので精度から考えて電圧5V対応、つまり500以下限定で作ってみましたが、9ビットという半端なデータ幅になってかえってぐちゃぐちゃなプログラムになってしまいました。
そこで、一応汎用性を求めて2バイト対応ですっきり作り直してみました。
恥ずかしながら自己流で書いて得意になってるところです。プロのみなさん、もっといい方法があってもしばらく教えてくれたりしないでね、がっかりするから。(^^; あ、でも、やっぱり、しばらくしたら教えてください。(^^)
1.2バイトBCD変換
世の中の常識のアルゴリズムとかいうの、こんなケースでどんなことになってるか私はまったく不案内です。
そこで十数年前のZ80のサブルーチン集の本を見たら(買ったきり読んでなかった。(^^; )、まず10000を何回引き算できるか、次に1000を何回引き算できるかという具合にとっても地道なことやってました。(^^;
まねしんぼで似たやり方をとりましたが、Z80に比べるとPICは命令が少ないだけかなり窮屈な感じはしました。
多少ともカッコよくキメてみようと、テーブル処理を使ってみました。引く方のデータはテーブルに並べておいて引っ張り出しています。
ルーチン回って、ずるずるとだらしなく引き算しなくて済むようになっています。
一方結果の格納はポインタ処理を使ってみました。
ターゲットアドレスをFSRレジスタに置いてからINDFレジスタに書き込むとターゲットアドレスに反映します。もちろん読み出しも同様です。
なかなかカッコいい。(自分で誉めてる。(^^) )
2.2バイト減算
2バイトの減算はBorrow(Carry)の分を上位から引いておく必要があります。ここだけ押さえておけばあとは簡単。
結果がマイナスになるケースはちいちゃいのからでっかいのなんて引き算する方が悪いと言うことにして放棄しました。Borrowフラグを立てただけ知らばっくれてます。Borrowフラグというよりギブアップの白旗です。(^^;
(Borrowがあってなおかつ上位がゼロのケースなんかがあって、もぉ頭ん中ごちゃごちゃになっちゃいました。 )
なお、PICでは減算の際Borrow(Carry)の振る舞いには注意が必要でした。引き算して「借り」ができるとCarryフラグに1が立つのではなく残数がある(「借り」ができない)と1が立つということになってるみたいです。通帳の残高フラグと思えばいいですかね。
BIN−BCD変換サブルーチン(BD2B)とそこで使う減算サブルーチン(SB2B)
1.準備:BDBINL、BDBINHに変換する2バイトBIN数を置いてBD2Bサブルーチン呼び出し
2.BDBINL、BDBINHをSBDL、SBDHに移してこれから10000を何回減算できるか、さらに1000を何回減算できるかを繰り返す
3.SBDL、SBDHが被減算数、SBSL、SBSHが減算数とする。SBSL、SBSHに10000、1000、・・・を置いてSB2Bを呼び出す
4.FIGに桁位置(10000の桁の時0、1000の桁の時1,1の桁の時4)、DGTにその桁の数値(減算できたカウンタ値)
5.10000、1000、・・・(2バイトBIN)は1バイトづつ分けてテーブルに並べておきFIG(桁位置)にしたがってテーブルから拾う
6.結果:DGT(減算できたカウンタ値=その桁の数値)はポインタの指すアドレスに格納する
BIN−BCD変換サブルーチン(BD2B)
;**************************************
;レジスタ
;PIC16F877なので余裕のレジスタマップ
;他のチップではやりくりが必要
SBDL EQU 044H ;被減算数エリア
SBDH EQU 045H
SBSL EQU 046H ;減算数エリア
SBSH EQU 047H
SB2BRF EQU 048H ;Borrowフラグ
BDBINL EQU 049H ;被BCD変換数エリア
BDBINH EQU 04AH
FIG EQU 04BH ;桁カウンタ
DGT EQU 04CH ;桁数値取得
SBDLR EQU 04DH ;SBDL保存
SBDHR EQU 04EH ;SBDH保存
D10000 EQU 060H ;結果を1桁毎に
D1000 EQU 061H ;ここに格納
D100 EQU 062H
D10 EQU 063H
D1 EQU 064H
;****************************************
;2バイトBCD変換
;被変換数をBDBINH BDBINLに置く
;結果はD10000〜D1に1桁毎に
BD2B
CLRF D10000 ;ここのクリアは特に
CLRF D1000 ;必要ではないが
CLRF D100 ;デバッグのとき前の
CLRF D10 ;データが残っている
CLRF D1 ;とわずらわしい
MOVF BDBINL,W ;BINデータ取り込み
MOVWF SBDL
MOVF BDBINH,W
MOVWF SBDH
MOVLW 060H ;initialize pointer(D10000)
MOVWF FSR ;(結果格納レジスタの最初)
CLRF FIG ;桁カウンタクリア
BD2BL0 CLRF DGT ;桁数値取得クリア
BD2BL1 MOVF FIG,W ;桁カウンタに相当する減算数
CALL TABLEL ;をテーブルから持ってくる
MOVWF SBSL ;最初は10000桁の下位バイトから
MOVF FIG,W
CALL TABLEH
MOVWF SBSH
MOVF SBDL,W ;被減算数保存
MOVWF SBDLR ;(数値により破壊される場合がある)
MOVF SBDH,W
MOVWF SBDHR
CALL SB2B
BTFSC SB2BRF,0 ;Borrowありでこの処理終了
GOTO BD2BL2
INCF DGT,F
GOTO BD2BL1
BD2BL2 MOVF SBDLR,W ;保存被減算数復帰
MOVWF SBDL
MOVF SBDHR,W
MOVWF SBDH
MOVF DGT,W ;ポインタの指すアドレスに書き込み
MOVWF INDF
INCF FSR,F
INCF FIG,F
MOVF FIG,W ;1桁まで行ったら終わり 残数が1桁の数値
SUBLW 04H ;最小桁(1)位置
BTFSS STATUS,Z
GOTO BD2BL0
MOVF SBDL,W
MOVWF D1
RETURN
;減算数テーブル
;10000、1000、100、10をBINで下位桁、上位桁に分けて置く
TABLEL ADDWF PCL,F ;減算数下位
RETLW 010H ;10000
RETLW 0E8H ;1000
RETLW 064H ;100
RETLW 0AH ;10
TABLEH ADDWF PCL,F ;減算数上位
RETLW 027H ;10000
RETLW 03H ;1000
RETLW 0H ;100
RETLW 0H ;10
減算サブルーチン(SB2B)
;************************************************************
;2バイト-2バイト減算
;SBDH_SBDL-SBSH_SBSL=SBDH_SBDL
;条件:結果は正かゼロ
;負になったらフラグを立てる(データは不正)
SB2B
CLRF SB2BRF ;Borrow FLAG クリア
MOVF SBSL,W ;
SUBWF SBDL,F ;SBDL-SBSL
BTFSC STATUS,C ;Borrowなら(C=0)スキップ
GOTO SB2BLP
MOVLW 01H ;上位を-1
SUBWF SBDH,F
BTFSS STATUS,C ;Borrowなければ(C=1)スキップ
GOTO SB2BBR ;Borrow(上位ゼロでかつBorrowありのケース
この場合演算放棄して結果不正)
SB2BLP MOVF SBSH,W
SUBWF SBDH,F
BTFSS STATUS,C ;Borrowなければ(C=1)スキップ
GOTO SB2BBR
RETURN
SB2BBR BSF SB2BRF,0 ;Borrowフラグ
RETURN
;*********************************************************
前回のプログラムに突っ込んで適当にデータを設定するプログラムを最初に置いて走らせると結果がD1000〜に並びます。
表示するならこれに30HをORしてキャラクタに変換すればいいはずです。
■今週のなるほど
テーブル処理はステッピングモータの波形データで経験済み。
ポインタ処理は間接アドレスが使えないことへの対応です。