お断わり(2018年現在)

このコーナーは「当局にとってはじめてのPIC」と言う意味で、そのトタバタ顛末記です。
使用したPICもツール類も古く、これからPICを弄ってみようと考えている各局様にとって読む価値が無いと思います。
よろしければ、「PICキットコーナー」を閲覧して頂ければ幸いです。





前回のPICへのファーストコンタクトから、今度は実用に使えるBCD変換ルーチンを考えてみる事にしたのですが・・・

よって今回はBCD変換ルーチンです。
結果的にメーカーライブラリがコンパクトで高速です。
個人が考える必要はなかったのですが、まあ趣味ですからちょっと考察を・・・

参考文献
①「PIC16F活用研究」 後閑 哲也 著  技術評論社
②ホームページ「電子工作の実験室」 後閑 哲也 著

仕様
2進化10進数は8桁表示、9999万9999で1桁4ビット、4バイト(32ビット)使用します。
16進数は6桁、FFFFFFで1桁4ビット、3バイト(24ビット)使用します。
因みに10進数に変換すると、1677万7215になります。
99999999(DEC)は5F5E0FF(HEX)の7桁の16進数となります。
ぎりぎりまでの10進数を表示させるには、28ビットの16進数が必要と思われますが、
周波数カウンタの場合、ゲートタイム1秒で16.7MHZまでしかカウント出来ない事となりますが、実際は10MHZ程度となると思われます。
リグの周波数表示ではゲートタイム1秒では使えないので、10ms程度とすると、100HZ以下切捨てて、6桁表示となりますので、16進数24ビット入力は実用上必要十分な仕様と思われます。



方法その①
16進各桁を10進数に変換し、加算する。
16進各桁は最大16カウントします。
そのカウント数×1カウントの10進数分を行い、それぞれを加算していきます。
プログラムイメージは簡単なのですが、サイズも大きくなり、はっきり言って、だるいです。

 FFFFFF(24ビット)
       1桁目 1×?
       2桁目 16×?
       3桁目 256×?
       4桁目 4096×?
       5桁目 65536×?
       6桁目 1048576×?

これは、最初の練習プログラムで作成した、10進アップカウンタの延長線上にあります。
よって、そのルーチンを少し変更するだけで、各桁用のルーチンが出来そうです。
それではと、16と256加算ルーチンをを作った所で、またもや飽きてしまいました・・・



方法その②
10進数各桁分を16進数から減算していく
これもその①同様です。

 FFFFFF(24ビット)
     -1000万×?
     -100万×?
     -10万×?
     -1万×?
     -1000×?
     -100×?
     -10×?
       残り

これは、その①で飽きてしまった状態では、作ってみる気力も起こりませんでした・・・

方法その③
ビットシフトを利用する方法
その①②の方法は自分でも思いつきますが、これは知りませんでした。
今回使用しているのは、文献その②で紹介されているルーチンで、メーカーのライブラリノート由来と言う事ですが、しっかり利用させて頂きました。
Microchip社のHPをちょっと探してみましたが、どこにあるのか判りませんでした。
でも便利なので、これを使えば問題無です。
プログラムサイズもコンパクトで、高速です。
で、そのルーチンを自分なりに解析してみました。



初期設定
C=0 キャリーリセット
シフトカウンタ shift=24(18h)
BCDデータクリア digit0-3(ここではD0~D3と表記)
入力16進数データエリア dspdt0-3(ここではH0~H2と表記)

ループ01 この順番で左シフト7回
C(=0)←D3←D2←D1←D0←H2←H1←H0←C(=0)
この1ループの中で、D0-D3それぞれ上下4ビットは、
5未満ならそのまま、5以上なら3を加える、補正処理を行なう。
このループを24回繰り返す
と至ってその工程はシンプルですが、何故BCD変換するのか理由が簡単には解りません。



ビットシフトの意味考察
16進バイナリで、1回左シフトで、その数値は2倍になります。
下記表は、
シフト前のビット列→左シフト後ぼビット列
となり、表の右最後の数字はBCD表記です。

表01
00=00H=(0000 0000B)→00=00H=(0000 0000B) = 00
01=01H=(0000 0001B)→02=02H=(0000 0010B) = 02
02=02H=(0000 0010B)→04=04H=(0000 0100B) = 04
03=03H=(0000 0011B)→06=06H=(0000 0110B) = 06
04=04H=(0000 0100B)→08=08H=(0000 1000B) = 08
05=05H=(0000 0101B)→10=0AH=(0000 1010B) = ×BCD表記不可、以下同じ
06=06H=(0000 0110B)→12=0CH=(0000 1100B) =
07=07H=(0000 0111B)→14=0EH=(0000 1110B) =
08=08H=(0000 1000B)→16=10H=(0001 0000B) =
09=09H=(0000 1001B)→18=12H=(0001 1100B) =
10=0AH=(0000 1010B)→20=14H=(0001 0100B) =
11=0BH=(0000 1011B)→22=16H=(0001 0110B) =
12=0CH=(0000 1100B)→24=18H=(0001 1000B) =
13=0DH=(0000 1101B)→26=1AH=(0001 1010B) =
14=0EH=(0000 1110B)→28=1CH=(0001 1100B) =
15=0FH=(0000 1111B)→30=1EH=(0001 1110B) =

補正処理(5以上は+3)した場合
表02
00+0=00H=(0000 0000B)→00=00H=(0000 0000B) = 00
01+0=01H=(0000 0001B)→02=02H=(0000 0010B) = 02
02+0=02H=(0000 0010B)→04=04H=(0000 0100B) = 04
03+0=03H=(0000 0011B)→06=06H=(0000 0110B) = 06
04+0=04H=(0000 0100B)→08=08H=(0000 1000B) = 08
05+3=08H=(0000 1000B)→16=10H=(0001 0000B) = 10
06+3=09H=(0000 1001B)→18=12H=(0001 0010B) = 12
07+3=0AH=(0000 0111B)→20=14H=(0001 0100B) = 14
08+3=0BH=(0000 1011B)→22=16H=(0001 0110B) = 16
09+3=0CH=(0000 1100B)→24=18H=(0001 1000B) = 18
10+3=0DH=(0000 1101B)→26=1AH=(0001 1010B) = ×BCD表記不可、以下同じ
11+3=0EH=(0000 1110B)→28=1CH=(0001 1100B) =
12+3=0FH=(0000 1111B)→30=1EH=(0001 1110B) =
13+3=10H=(0001 0000B)→32=20H=(0010 0000B) =
14+3=11H=(0001 0001B)→34=22H=(0010 0010B) =
15+3=12H=(0001 0010B)→36=24H=(0010 0100B) =



以上より、
16進バイナリは1回左シフトすると、そのまま2倍される。
10進バイナリは補正処理をすれば、1回左シフトするとBCD表記で2倍される
じゃあどの様に応用すれば良いのか、当局の生CPUではパッと思い浮かびませんが・・・
と言う訳で、実働のルーチンを参考に解析してみる事に・・・
プログラム イメージ
入力16進バイナリデータ HEX(1バイト)
出力10進BCDデータ DEC0、DEC1(2バイト)
ループカウンタ SHIFT
作業用ワーク WORK1、WORK2

初期設定
DEC0=0、DEC1=0をセット
SHIFT=8をセット

LOOP(ラベル)
RLF HEX ;HEXを左シフト
RLF DEC0 ;DEC0を左シフト
RLF DEC1 ;DEC1を左シフト
SHIFT=SHIFT-1
IF SHIFT=0 THEN RETURN(終了)

ADJST(ラベル)
W=DEC0
CALL ADJBCD
DEC0=W
W=DEC1
CALL ADJBCD
DEC1=W
GOTO LOOP

ADJBCD(ラベル)
WORK1=W
W=W+3
WORK2=W
IF WORK2>7 THEN WORK1=W
W=30H
W=W+WORK1
WORK2=W
IF WORK2>70 THEN WORK1=W
W=WORK1
RETURN
2桁(8ビット)16進を12ビット10進(BCD)に変換のシミュレート

いきなり24ビット変換では大変なので、8ビット変換をイメージしてみました。
16進の変換される値は、とりあえずFFHとしてみる。
初期値 HEX(8bit)=FFH、DEC(12bit)=000
8ビット16進数なので、左シフトループは8回
8ビット16進は4ビットそれぞれ左シフトするだけで、下位ビットは0がCフラグよりシフトしてくる
12ビットBCDエリアは下位4ビットから順にそれぞれに左シフトするが、
左シフト後に5以上の4ビット列(桁)は+3される事に注意

start---(↓繰上ビット000H)--(↓被変換ビット FFH)
ビット列 (0000 0000 0000)←(1111 1111)

1回目--(↓DEC 12bit 001H)--(↓HEX 8bit)
ビット列 (0000 0000 0001)←(1111 1110) 左シフト後5以下で補正無し

2回目--(↓DEC 12bit 003H)--(↓HEX 8bit)
ビット列 (0000 0000 0011)←(1111 1100) 左シフト後5以下で補正無し

3回目--(↓DEC 12bit 007H)--(↓HEX 8bit)
ビット列 (0000 0000 0111)←(1111 1000) 左シフト後、3-0bitは5以上で補正必要

3回目--(↓DEC 12bit 00AH)--(↓HEX 8bit)
ビット列 (0000 0000 1010)←(1111 1000) 上記を+3補正

4回目--(↓DEC 12bit 015H)--(↓HEX 8bit)
ビット列 (0000 0001 0101)←(1111 0000) 左シフト後、3-0bitは5以上で補正必要

4回目--(↓DEC 12bit 018H)--(↓HEX 8bit)
ビット列 (0000 0001 1000)←(1111 0000) 3-0bitを+3補正後

5回目--(↓DEC 12bit 31H)--(↓HEX 8bit)
ビット列 (0000 0011 0001)←(1110 0000) 左シフト後、5以下で補正無し

6回目--(↓DEC 12bit 063H)--(↓HEX 8bit)
ビット列 (0000 0110 0011)←(1100 0000) 左シフト後、7-4bitは5以上で補正必要

6回目--(↓DEC 12bit 093H)--(↓HEX 8bit)
ビット列 (0000 1001 0011)←(1100 0000) 7-4bitを+3補正後

7回目--(↓DEC 12bit 127H)--(↓HEX 8bit)
ビット列 (0001 0010 0111)←(1000 0000) 左シフト後3-0bitを補正必要

7回目--(↓DEC 12bit 12AH)--(↓HEX 8bit)
ビット列 (0001 0010 1010)←(1000 0000) 左シフト後3-0bitを+3補正後

8回目--(↓DEC 12bit 255H)--(↓HEX 8bit)
ビット列 (0010 0101 0101)←(0000 0000) 左シフト後、終了

以上、ちゃんとBCD変換されました。
が、理屈がイマイチ良く分かりませんですね・・・(-_-;)




上記シミュレーションの結果を、当局の適当なるイメージ解析してみました。

①16進バイナリは1回左シフトすると、そのまま16進バイナリで2倍される。
②10進バイナリは補正処理をすれば、1回左シフトするとBCD表記で2倍される
③上記②の効果は1回きりで、左シフトする度に行なわなければならない。

まず、この事実より、
8ビット16進数は8回左シフトで、2^8(2の8乗の意)されます。
2^8=256=100Hです。
ここで、8ビットの16進数FFHがあったとするならば、8回シフトによって、(注1)
すなわち、FFH×100H=FF00H(=65280)と2バイトの16進数となります。
しかしこれでは何の役にも立ちません。
では繰り上がりの2バイト目以降に対して、補正処理をしながら左シフトするならば、
10進補正されながら、1回の左シフトに付き、2倍される事になります。
それを8回行えば、10進補正されながら、2^8される事になります。
しかし、10進補正のため、桁上り分は2バイトとなります。
同様の方法で16ビット16進数も16回ループで可能と思われますし、
24ビット、32ビット16進数の変換も可能と思われ、極めて実用的な変換方法と思われます。
と言う感じで、無理やり納得する事にしました。

疑問点
表02より、補正による2倍(左シフト)は09Hまでです。
補正する前に10H以上になると破綻してしまいます。
そこで、上記シミュレーションより検証してみると、
繰り上がりビットに対して補正し、被変換ビットは左シフトのみです。
以上より、最大数のFFHの時でも、いきなり出現するのは最大7Hですので、
この時点で、補正処置が行なわれ、A0Hとなりますし、
変換の破綻や+3加算時の上位桁ビット列への繰り上げも無く、大丈夫の様です。

注1)左シフトにより、下位ビットに入ってくるのは"0"とする。
   実際、元の16進数部分のビットは切り捨てるので、何でも良いのですが。


↑入力モード初期状態




↑FFFFFFHを入力




↑10進に変換表示



実験用ソースファイル bcd_test0.asm
実験課題
6桁16進数を8桁10進数に変換表示する
*16進入力モード
 LED1桁目のドットをカーソル代わりに点滅させながら、16進数入力待ち
 SW3で1つの桁のデータを1桁目で入力
 SW4で1つの桁のデータ決定、繰上げ
 SW5で10進変換し、10進表示モードへ移行
:*10進表示モード
 SW4で16進入力モードへ移行

実験回路および実験プログラム
今回は前回の実験用回路をそのまま使用しており、プログラムが違うだけです。
よって、ハードウエア製作はありません。

プログラム構成
6桁16進数ライン・インプット・サブルーチン(新規)
24ビット16進数をBCD8桁に変換するサブルーチン(参考文献より)
7セグLED表示サブルーチン(前回とほぼ同じ)

その他改善点
スイッチスキャンのフィーリングが悪かったので、改善しました。
カーソル点滅を数字を表示しながらの点滅に変更

問題点
パッと見LEDのチラツキが気になります。
特に8桁表示なると、見にくいです。



ソールファイルはそのまま見ると、何故か表示がズレてしまいます。
プロジェクトに登録すると何故かちゃんと並ぶ様です。
勝手にTABの幅や並び回数や変わるようですが、今の所原因不明です。
戻る  実験・製作の記録TOPへ戻る