2バイトデータのビットシフトについて

このQ&Aのポイント
  • PIC 12F683を使った温度ロガーのプログラムにおいて、EEPROMからのデータ読み出し部分でビットシフトを行なっているが、その仕組みがわからない。
  • デフォルトの左詰め10ビットのAD変換データをビットシフトして取り出す方法が理解できない。
  • 具体的かつ詳細にビットシフトの仕組みを教えてほしい。
回答を見る
  • ベストアンサー

2バイトデータのビットシフトについて

PIC 12F683 をつかった温度ロガーのプログラムについて、 質問させていただきます。 こちら http://www8.plala.or.jp/InHisTime/page179.html#PIC-144 のプログラムなのですが、mikroC のソース、 http://www8.plala.or.jp/InHisTime/PIC-144/thermo_logger_v3.c の、EEPROMからのデータ読み出し部分で、 EEPROM_24LC1025_Page_Read(addr, buf, 2); ad = (buf[1] << 8) + buf[0]; このように、読み出したデータ(buf)に対して、 ビットシフトをしているのですが、 このようにして、データが取り出せることの、仕組みがわかりません。 たとえば、AD変換のデータが、デフォルトの左詰め10ビットとして、 buf[1] : 1111 1111 buf[0] : 1100 0000 というデータだとして、上記のビットシフトで、 なぜデータが取り出せるのかが理解できません。 どなたか、よろしければなるべく具体的に、 お教え願えませんでしょうか。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.6

配列が確保される場合、 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]. (メモリアドレス   Low → High ) となるはずです。 質問文のデータ buf[1] : 1111 1111 buf[0] : 1100 0000 を当てはめると、 1100 0000  1111 1111 の順に並んでいて、adに直接代入すると ad = 1100 0000 1111 1111 となり、期待とは違う結果になってしまいます。 そのため、   1111 1111 0000 0000 (左8ビットシフトしたbuf[1]) +        1100 0000 (buf[0]) -----------------------   1111 1111 1100 0000 (これをadに代入) という操作をしていると思われます。 なお、ビットシフトの操作で元のデータが変わってしまうようにお考えのようですが、 「演算のために、仮に持ってきたデータ」 に対してシフト操作をしているだけなので、元のデータに変化はないと考えます。 ※プログラミングに関しては、かなりのブランクがあるので、間違っていたらごめんなさい。

phuseman
質問者

お礼

自分が理解できていない箇所が明確に分かりました。 ビットシフトしても値のサイズは変わらない、と考えていたのです。 左8ビットシフト前 1111 1111 左8ビットシフト後 1111 1111 0000 0000 ではなく、 左8ビットシフト前 1111 1111 左8ビットシフト後 0000 0000 と、ビットシフト前後で値は8ビットのままという考えに固執していたので、 何のためにそんな事をするのかが理解できませんでした。 8ビットシフトによって、8ビットのデータが16ビットになるわけですね。 (ようやく、<< 8 、で256倍になるだけ、というみなさんの回答の意味が分かりました。) あまりに基礎的な理解不足で恐縮です。 今では、それぞれの回答はすべて的を射ているのが分かります。 回答をいただいた皆さん、ありがとうございました、非常に助かりました、 今後も何度かこのテキストを見て理解を深めようと思うので、保存しました。 システム上、すべてBAにするわけにはいきませんので、 個人的に理解し易かった kuma310min さんをBAとさせて いただきます。 回答をいただいたみなさん、本当にありがとうございました。

その他の回答 (7)

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.8

すみません、正確ではないですね 正確には ad = buf[1]は、adに代入してでは無く  内部的な int のバッファに代入して、という意味に訂正

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.7

[A] ビットシフト前が buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]. pppp pppp, qqqq qqqq, xxxx xxxx, yyyy yyyy, zzzz zzzz, 0000, 1100, 1010 1111. こうである場合、 ---ソース引用部--- ad = (buf[1] << 8) + buf[0]; ------------------ を分解して書くと、 ad = buf[1]   : ad = 0000 1100 ad = ad << 8  : ad = 11 0000 0000 ad = ad + buf[0]: ad = 11 1010 1111

  • tsunji
  • ベストアンサー率20% (196/958)
回答No.5

>10101111(10進:175) x 256 + 1100 0000(10進:192) >= 44800 + 192 >となってしまいますので、もとの 703 が得られない データは10ビットで、左詰めなので、 44800+192=44992 でさらに、6ビット右シフト(64で割る)と 44992÷64=703 となるのです。 右詰めならそのままですね。

phuseman
質問者

お礼

ビットシフト後も、8ビットのままだと勘違いしていました。 1111 1111 が 1111 1111 0000 0000 と 16 ビットの値になるわけですね、ようやく理解できました。 回答ありがとうございました。

回答No.4

buf と ad は、それぞれ unsigned char buf[6]  (0~255) unsigned int ad   (0~65535) で定義されています。 adに読み込む際、直接(変換なしに)2バイトデータとして読み込めれば、 このような変換は必要ないのでしょうが、考えられる原因として、 ・(ハードウェア的な理由により?)1バイトずつしか読み込めない ・2バイトまとめて読むことは可能だが、データの格納が上位・下位逆になっている があります。 (ちょっと見た感じでは、後者っぽい?) 後者であれば、格納データが、 buf[0] buf[1] ・・・ buf[6] の順に格納されていて、buf[1]が上位・buf[0]が下位の2バイトデータで読みだしたいが、 そのままだと逆になってしまう事が考えられます。 この場合、 ・buf[1] を8ビットシフトして、上位バイトへ ・それにbuf[0]を加算 して、adを 「buf[1]が上位・buf[0]が下位の2バイトデータ」 にしていると思われます。

phuseman
質問者

補足

たとえば、AD変換の結果、 1010 1111 11 (10進:703) という10ビットのデータが得られ、 buf[0]とbuf[1]が逆に格納されると仮定した場合、 ---ソース引用部--- ad = (buf[1] << 8) + buf[0]; ------------------ の演算は、 [A] ビットシフト前 buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]. pppp pppp, qqqq qqqq, xxxx xxxx, yyyy yyyy, zzzz zzzz, 1100 0000, 1010 1111. [B] ビットシフトした値 buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]. qqqq qqqq, xxxx xxxx, yyyy yyyy, zzzz zzzz, 1100 0000, 1010 1111, 0000 0000. のように、[B] のbuf[1]と、[A]のbuf[0]は同じになってしまう気がするのですが、 間違いがあれば、ご指摘願います。

  • tsunji
  • ベストアンサー率20% (196/958)
回答No.3

>ad = (buf[1] << 8) + buf[0]; この計算式は、  ad = (buf[1] * 256) + buf[0]; と同じ事をやっているのですよ。

phuseman
質問者

補足

ご回答ありがとうございます。 たとえば、AD変換の結果、 1010 1111 11 (10進:703) という10ビットのデータが得られ、 buf[1] : 1010 1111 buf[0] : 1100 0000 と格納されたとして、 10101111(10進:175) x 256 + 1100 0000(10進:192) = 44800 + 192 となってしまいますので、もとの 703 が得られない (バイトが逆順でも、やはり703は得られない)ような気がします。 間違いがありましたらご指摘願います。

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.2

>左に8ビットシフトし、加算することの意味自体が分かりません。 0000 0010の2進数(10進数表記で2)を1ビット分左シフトすると… 0000 0100になります。10進数表記で4ですね。 値が倍になっています。 では… 0000 0010の2進数(10進数表記で2)を3ビット分左シフトするとどうなるでしょう? 0001 0000になります。10進数表記で16です。 値が8倍になっています。 ということで、左シフトした場合は2のn乗で倍になります。 1ビット左シフトなら2の1乗で2倍。 2ビット左シフトなら2の2乗で4倍。 3ビット左シフトなら2の3乗で8倍。 4ビット左シフトなら2の4乗で16倍。 5ビット左シフトなら2の5乗で32倍。 6ビット左シフトなら2の6乗で64倍。 7ビット左シフトなら2の7乗で128倍。 8ビット左シフトなら2の8乗で256倍。 9ビット左シフトなら2の9乗で512倍。    : では…… >左に8ビットシフトし、加算することの意味自体が分かりません。 するとどうなりますか?

phuseman
質問者

補足

確かに、演算としては 2^n乗となりますので、 2^8 = 256倍して加算する、という事になるのですが、 たとえばAD変換の結果、 1010 1111 11 (10進:703) という10ビットのデータが得られ、 buf[1] : 1010 1111 buf[0] : 1100 0000 と格納されたとして、 buf[1] << 8 で、1100 0000(10進:192) が得られると思うのですが、 それと buf[0] は同じ値(buf[0]とbuf[1]が逆に格納されていたとしても、やはり同様の結果) のような気がするのですが。 間違いがあればご指摘願います。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

その値で計算すると ad はいくらになりますか?

phuseman
質問者

補足

さっそくのご回答ありがとうございます。 2進で 1111 1111 11、10進で 1023、16進で 3FF だと思います。こちらの、 http://homepage3.nifty.com/mitt/pic/pic683_1.html#conf ADCON0.ADFM が指定されていないので、デフォルトの左詰と考えると、 b[1] : 1111 1111 b[0] : 1100 0000 とbufに格納されると思うのですが、 たとえば、2バイトのデータをバイトごとに区切って 整数で加算しても、最大値は、 1111 1111 (255) + 1111 1111(255) = 510 が最大で 1023 にはならないと思うので、 左に8ビットシフトし、加算することの意味自体が分かりません。

関連するQ&A

  • エラーが出てしまいます。

    このプログラムはPIC16F88でYMZ294を使って「かえるの歌」を流すプログラムですが、間違ってるところを教えてください。 http://www8.plala.or.jp/InHisTime/LABO-028/ymz294.c ソフトはmikroC PRO for PIC (v.4.15.0.0) です。 実際にURL先のプログラムをコピーしてmikroCに貼り付けて ビルドさせると下の欄にエラーが表示されます。

  • 配列をnビットシフトする

    題名の通り配列をnビットシフトする方法を教えてください。 char buf[3] = { 0x30,0xf0,0x80 }というデータで n=3 だった場合 欲しいデータはbuf[3] = { 0x06,0x1e,0x10 }です。 { 0x30, 0xf0, 0x80 } →{ 0x06, 0x1e, 0x10 } [ 00110000,11110000,10000000 ]→[ 00000110,00011110,00010000 ] ご教授お願いします。

  • PICのシリアル通信のWIN_APIプログラムについて

    PICとPCとのシリアル通信の WIN_APIプログラムについてどなたかご存知の方は教えてください. 現状,API関数を用いて,シリアル通信をできるようにプログラムを作成したのですが,送信はできるのですが,受信データが"C0"をPICから送付しているのに,"FFFFFFFC0"と受信されてしまいます. 先の"FFFFFFF"をとるにはどうしたらいいでしょうか? 現状:受信プログラム // シリアルポートに対する書き込み(PICに送信命令) WriteFile(hCom, w_com_Buf,lstrlen(w_com_Buf), &n, 0 ); // シリアルポートに対する読み込み ReadFile(hCom, s_com_Buf,1, &n, 0 ); mikroC上のUSARTターミナルでは送受信は適正で, 上記のプログラマで 送信はPIC側で受信していることは7SEGを使って確認しています. しかし,PC側では, s_com_Buf[0]には,"C0"が入ったり,"FFFFFFC0"が入ったりします. PICは現状,多チャンネルAD変換をしているため, 送信に対する受信データのやり取りができなくて困っています. どなたか先生教えてください.

  • PIC12F683からパソコンへのシリアル通信

    PIC12F683 を使って、簡単な電圧ロガーを作ろうとしているのですが、 パソコンへの送信が正常に行われません、わかる方がおられたらどこが悪いのかご指摘願います。 PICの書き込み、PICからのデータ受信に用いているパソコン(のマザーボード)は、G45GCMX-S2です。 参考にさせていただいているサイトは、 http://www8.plala.or.jp/InHisTime/page005.html#PIC-002 なのですが、上記サイトのソースプログラム http://www8.plala.or.jp/InHisTime/PIC-001/DataLogger2.c には何の手も加えず、動作させております。 また、PIC->PCへの接続端子の接続が正常である事も、テスターで確認しております。 (回路から、PCのCOM1(DSUB-9pin)へ接続。) コンパイル(MikroC 8.88)、hexの書き込み(RCDライタ)までうまくいくのですが、 ブレッドボードに組んで、電源を入れてもGP5の電圧に変化が無い、という状況です。 (LEDを点滅させるプログラムを書き込んだ場合は問題なく動作しています。) 実際の各端子は Vdd 1pin : 5V GP5 2pin : open (※ この端子の電圧に変化が無い) GP4 3pin : open GP3 4pin : 5V CPP1 5pin : open AN1 6pin CH1 : open AN0 7pin CH0 : open Vss 8pin : GND となっています。テスターで測ると 2pinは2.3Vで一定、 レベル変換用のトランジスタのコレクタは3.4V一定です。 念のためレベル変換回路 (http://www8.plala.or.jp/InHisTime/img2022.jpg の右上、C1815の部分。) 単体の動作確認はしています。 ハイパーターミナルその他のソフトで通信しようと試みたのですが、 反応が無いため、ピンの電圧を測ってみたところ、パソコンへの送信出力ピンの電圧が 変化していないので、これは正常じゃないのでは無いか?と思い、 詳しい方のアドバイスをいただこうと投稿しました。 上記のソースであれば、1秒ごとに出力電圧に反応があるかと思いますが、無反応です。 説明がややこしくなりましたが、わかる方がおられましたらよろしくお願いします。

  • ビット演算を学びたい

    a &= 2; a |= 2; a ^= 2; a ~= 2; a <<=2; a >>=2; みたいな感じでビット演算が使われているソースを 良く見るのですが、いまいちビット演算で何をしているのかが 分かりません。 参考書などには文字通りビットをいじるような旨のことが書いてあります。 (こちらにも同じようなことが http://www9.plala.or.jp/sgwr-t/c/sec14.html) こういうので何となくは分かるのですが、 実際にこれを何に使えるか、実践ではどのように使うのかが なかなか見えてきません。 このビット演算を私のような者でも実際のプログラムで使いこなせるように なれるようなサイトや書籍の提示、あるいはサンプルのプログラムなどで ご指導いただけたらと思います。

  • EEPROMをPICで使用する方法について

    PIC16F87XでEEPROM(24LC256)の利用を実験しているのですが、EEPROMへ連続してデータを続書き込みする場合、完了時に一定の時間ウェイトする必要があるようです。 データシートにWrite cycle time(byte or page) Tsp = 5msトありますが、この5ms秒のウェイトとは、次回の書き込みまでに、AD変換など他の処理が、5ms以上時間がかかるれば、問題ないということでしょうか? それとも、この間は、他の処理は行ってはいけないということでしょうか? どのカテゴリに質問していいのかわからないので、とりあえずこのカテゴリに質問させていただきます。 PICの処理に詳しい方いらっしゃいましたら、よろしくお願いします。 ちなみに、開発は、CCS-Cを使用しています。

  • pic32mxのデータ幅

    初めてPIC32MXでプログラムを走らせようとしています。 MPLAB X IDEとXC32を使っています。 PIC18F等の8ビットCPUの経験はあります。 PIC32MXはアドレス幅32ビットデータ幅32ビットだと思ってプログラムを作り始めました。 デバックするところになって分からないことが出てきました。 XC32におけるint型のデータ幅は32ビット、unsigned char型は8ビットです。 プログラムで変数を定義するとunsigned char型は偶数アドレス、奇数アドレスそれぞで定義されます。 int型の配列を定義すると、偶数番地アドレスを先頭として4バイト単位で割り当てられます。 これは、PIC32MXがバイトアドレッシングCPUで4バイトアライメントということだと思います。(違ったらご指摘下さい。) 実際MPLAB X IDEでデータメモリを参照すると1アドレス1バイトデータで表示されます。 質問は、 PIC32MXは32ビットアドレス空間(実際に持っているROM、RMAサイズは別として) のCPUではあるが、メモリ幅は、8ビットというのではないだろうか? というものです。PIC32MXの内部レジスタ関連は32ビットなので32ビットCPUだとは思いますが、データシートにある「 32 ビットのネイティブデータ幅」という意味がわかりません。 実際のメモリ空間は、アドレス範囲 x 8ビットということでいいのでしょうか。 御教授いただけましたら幸いです。

  • MplabのRead_EEPROM

    Mplab8.92+PICkit3でPICのプログラムをしています。 ポート入力をPIC内蔵のEEPROMに書込むようなプログラムを作り ターゲットボード上で動作させた後、PICをボードから抜き取り Mplab上でReadしViewメニューのEEPROMで見ると ポートの入力とは異なる値になっています。 (ボードの入力はプルアップした上で固定してあります。) しかし、A社のプログラマーでReadすると ポート入力がEEPROMに正しく書込まれています。 (A社のプログラマーはサポートのデバイスが少ないので 現実的には使えないのですが。) 色々試してみると プログラムとして、「PIC内蔵EEPROMデータメモリに初期値として書込んだデータ」は 正しく、書込み、読取できましたが ボード上で動作しないと確定しないようなデータ (ポートの入力、カウント回数・・・等) は、正しく書込まれている (プログラムの動作から確認すると) けれどもMplab上のReadでは正しく表示されませんでした。 MplabのReadとはこういうものなのでしょうか? (私の推測ではMplabのRead-View-EEPROMはCPUを実機で 動作させることなくパソコン上でシュミレーションするための もののように思えてきたのですが・・・) それとも、何か設定すれば可能になるならば 教えてください。 私の場合Mplabを使う場合 シュミレーションとかは使いません。 コンパイルしてCPUに書込み(Program)するだけです。

  • 不揮発メモリ(EEPROM)内蔵のワンチップマイ…

    不揮発メモリ(EEPROM)内蔵のワンチップマイコンについて http://mori.nc-net.or.jp/EokpControl?&tid=154077&event=QE0004 に非常に奇妙な質問と回答がありました。 回答が締め切られたため、別話題として質問にしてみました。 不揮発メモリのEEPROMには書きこみ回数に寿命制限があります。 このため、ワンチップマイコンにEEPROMを内蔵すると、書きこみ回数の 寿命制限を越えたときから、マイコンは正常に動作できなくなる問題 があります。 従って、EEPROM書きこみ回数制限を越えても、CPUが動作できるように、 不揮発情報をCPU外部のNVRAM RAMに持たせるという考え方がH8ではとられ ていると思われます。 一方PICマイコンで内蔵EEPROMを使用するには当然、その書きこみ回数が 書きこみ制限回数を越えないような応用に使わないと、PICマイコンは EEPROM書きこみ回数の上限値を越えたときから恒久故障に至ります。 そうしたマイコンそれぞれの設計思想を理解せずして、PICマイコンが良い とか、H8が悪いみたいな結論は、マイコンとその応用の仕方の考え方の 理解が全くできていないと思いますよ。 H8が悪くてPICが良い? http://mori.nc-net.or.jp/EokpControl?&tid=154077&event=QE0004

  • PIC16F1827のEEPROMへの書込み

    PCM V5.0(PIC用CCS社コンパイラ)を Mplab8.92に組込み、PICkit3を使ってプログラムしています。 PIC16F1827のEEPROMへの書込みがうまくいきません。 RAは入力ポートで固定してあります。 RBは出力ポートでLEDが接続してあります。 テストプログラムは一回のみの動作です。 実機で動作させると RAは固定してあるので当然(4)でLEDが点灯しますが、 実機からCPUを抜き取りMplab上でReadすると EEPROMのアドレス0,1,2は異なるデータであり 動作させるたびに違うデータが書込まれています。 しかし (1) a0=35h (2) a1=a6h (3) a2=93h と定数にすると EEPROMのアドレス0,1,2は正しいデータが 書込まれています。 Q1. a0,a1,a2をポート入力とする場合は CPU自体に何か設定する必要があるのでしょうか? それともプログラムに工夫が必要なのでしょうか? いままで使っていたPIC16F88では このようなことはなかったと思うのですが・・・。 ----- テストプログラム ----- a0 = RA; //(1) write_eeprom (0,a0); a1 = RA; //(2) write_eeprom (1,a1); a2 = RA; //(3) write_eeprom (2,a2); if (a0 == a1 && a1==a2) //(4) RB =255; else RB =0;

専門家に質問してみよう