メッセージ

2016年08月16日の記事

2016/08/16(火)BCD ⇔ 符号なしバイナリ変換

システム制御系、特にファームウェア制作ではよく出てくるにも関わらず、
毎回調べ直すという非効率なアホ作業をしでかしているので、自分メモ的にまとめてみようと・・orz

BCD コードというのは Binary Coded Decimal の頭文字で、日本語の古い文献などには「二進化十進数」とも表記されます。
数字を表示する機器などには未だ広く使われているようです。
BCDコードとバイナリコードの相違が理解できない方は、まずそれを他所で理解してください。理解できていないと、以下の記事の内容は全く理解できないでしょう。

今般の制作ボードの中にBCDコードを要求するLSIがあり、BCDコードとバイナリコードの相互変換が必要です。必要なのは 0~99の値なので、それに特化しています。

難しいのは、バイナリ → BCD変換のほうで、実に力ずくのアルゴリズムから、難解なものまでいろいろな方法があるのですが、応用が利くのは以下の、
『BCD部分の各桁について、「値が5以上ならば3を加算する」』
を基本にする手法。
以下を参考にしています:
http://www.geocities.jp/leitz_house/electronics/pic/bcd_01.htm
http://minkara.carview.co.jp/userid/526128/blog/24236882/
〔覚え書き:2進数 ⇒ BCD変換について…〕

オリジナルは、どうやらキャッシュしか残っていないようで、いつ消えるか判りません。
こんな方法で本当に出来るのか? という疑問を持たれる方が大半と察しますが、数学的見地でも本当に出来るのですから、科学の基礎というのは奥が深いです。
実際にC言語にて作ったものが以下(ビックエンディアン環境にて動作確認済):
/**  cnv_byte_to_bcd 1バイトバイナリデータを1バイトBCDに変換 */
unsigned char cnv_byte_to_bcd(unsigned char bval) {
  union {
    struct {
      unsigned char bcd ;          // 変換後の値
      unsigned char hex ;          // 変換対象バイナリ
    } conv ;
    unsigned short  buf ;
  } convbcd ;

  unsigned char bitcnt ;

  if (bval >= 100) return (bval) ; // バイナリ値 100 以上は BCD に変換不可のため、そのままリターン。
  convbcd.buf = 0 ;                // 使用領域は予めゼロクリアしておく。
  convbcd.conv.hex = bval ;        // 変換対象のバイナリ値を置数。

  for (bitcnt = 0 ; bitcnt < 8 ; bitcnt++) {
    if (((convbcd.conv.bcd & 0x0f) + 0x03) >= 0x08) convbcd.conv.bcd += 0x03 ;
    if (((convbcd.conv.bcd & 0xf0) + 0x30) >= 0x80) convbcd.conv.bcd += 0x30 ;
    convbcd.buf <<= 1 ;
  }
  return convbcd.conv.bcd ;
}
肝になる部分は、union 共用体の部分で、メンバ bcd と メンバ hex の順番は重要です。
対象ターゲットCPUは、ビックエンディアンで、その環境で動作確認しています。
インテルやAMDのCPUだと、殆どがリトルエンディアンなので、メンバ bcd と メンバ hex の定義を逆にしないと駄目かもしれません。

一方で、BCD → バイナリ変換は簡単です。4ビットごとに区切り、上位4ビットの値を×10し、下位4ビットをそのまま加算すると変換完了です。
/**  cnv_bcd_to_byte 1バイトBCDデータを1バイトバイナリに変換 */
unsigned char cnv_bcd_to_byte(unsigned char bval) {

  unsigned char convbin ;

  convbin = ((bval & 0xf0) >> 4) * 10 + (bval & 0x0f) ;
  return convbin ;
}