第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してキャラクタに変換すればいいはずです。





今週のなるほど

テーブル処理はステッピングモータの波形データで経験済み。
ポインタ処理は間接アドレスが使えないことへの対応です。