• ベストアンサー

6バイトを1フレームとして4つまで格納するキューでは25バイトキューは必要?

いま、PICマイコンの12F683のためにこれを作っているのですが、使い方としては6バイトのデータを1つのフレームとして受信できたら、それをキューに入れていこうと思っているのですが、最大4フレームまでストックできるようにしたいと思っていて、いっぱいになったらUARTで1フレームずつ出力するつもりです。 6バイトを4フレームなのでMAX_BUF_NUMは”24”に設定したのですが、これをやると今のプログラムは4フレーム目を格納しようとした時に、最後のバイトが格納できなくなってしまうことがわかり”25”としているのですが、24 + 1としなければいけないのでしょうか? int CountQueue() { if(Head <= Tail) return(Tail - Head); else return((MAX_BUF_NUM - 1) - Head + Tail); } void enqueue(BYTE x) { Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ if(Tail==Head){ QueueState = QSTATE_FULL; return; } UartSendBuf[Tail]=x; if(CountQueue()==(MAX_BUF_NUM-1)){ QueueState = QSTATE_FULL; } return; } BYTE dequeue(void) { BYTE x; if(Head==Tail){ QueueState = QSTATE_EMPTY; return; } Head=(Head+1)%MAX_BUF_NUM; x=UartSendBuf[Head]; UartSendBuf[Head]=0x00; //出力し終わったら0x00でクリアしておく。 return x; }

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

  • ベストアンサー
  • 6yemon
  • ベストアンサー率69% (25/36)
回答No.4

CountQueue()が、格納してるデータの個数をHeadとTailから計算しようとしてますね。これが本質的な原因のように思えます。 大きさが24の配列を使いきろうとすれば、データが皆無である場合も、データが満杯である場合も、どちらの場合も Head == Tail です。そこを区別するために、常に一個以上データのない場所を空けておく方法があります。この場合、24個のデータに対して、大きさが25の配列が必要になります。 皆無と満杯を区別するもう一つの方法はフラグを設けることです。No.3の回答とあなたのコードを比べてみれば、QueueStateフラグの使い方に違いがあることがわかるでしょう。 しかし、私ならQueueStateフラグを使いません。代りに、格納されているデータの個数をカウントする変数を設けます。その変数は、初期値が0(=キューが空)、enqueue()で+1し、dequeue()で-1する、こうすればCountQueue()はその値をreturnするだけです。 しかもこの変数を見れば、キューが空であることも満杯であることも、すぐ判定できます。これで処理全体がすっきりするはずです。 その他、気づいた点。dequeue()はBYTE型の値を返す関数なのに、異常時に単にreturn;してます。こういう事をしてると後で痛い目に遭います(笑)。 このようなケースでは次のどちらかの方法をとります。 (a)正常時には決して返すことの無い値(0とか-1など)をエラーとして返す。 しかし表現可能なすべての値がデータとしてありうる場合はこの方法をとれないので、 (b)関数が返す値を成功か失敗か(或いはエラーの種類)だけにする。 データを返すためのポインタを引数として渡してもらい、例えば int dequeue(BYTE *x) として、*x = UartSendBuf[Head]; でデータを返します。

techhouse
質問者

お礼

回答頂きありがとうございました。返事が遅くなりましてすみません。 実は、回答頂いた内容で > (b)関数が返す値を成功か失敗か(或いはエラーの種類) > だけにする。 > データを返すためのポインタを引数として渡してもらい、例えば > int dequeue(BYTE *x) > として、*x = UartSendBuf[Head]; でデータを返します。 この内容に関する意味を理解することができずにいまして、 特にポインタを使っていることで、自分のポインタの知識が乏しいことに気づき”http://dell-support.okwave.jp/qa5507699.html”でこの部分に関して別個に質問させて頂いておりました。 そこで、ポインタについてググッていたら http://www9.plala.or.jp/sgwr-t/c/sec10.html というページを見つけ、やっと理解することができるようになった気がします。 引数には、”*x = UartSendBuf[Head];”このプログラムで取得したデータを代入したい引数のアドレスを設定していたんですね!! それで、dequeue関数の戻り値は、このキューのステート状態を取得できるし、また同時にデータも取得するためにポインタを使っているんですね。これは便利ですね。 もう少し、ポインタに関する知識をつけたいと思います。

その他の回答 (3)

回答No.3

No.2 です。 一部訂正。 enqueue の、キュー・フルの判断が間違っていました。 やっぱり、横着は良くないですね。 キュー・フルの判断は、「次に書こうとするところが、 次に読むはずのところ」なので、 void enqueue(BYTE x) { if (QueueState == QSTATE_FULL) return; UartSendBuf[Tail]=x; Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ if(Tail == Head ) QueueState = QSTATE_FULL; else QueueState = QSTATE_VALID; //かってにつけました。 } です(たぶん) ただ、これは、「書いてしまってから、これで一杯」 と通知するパターンです。 書こうとしたら一杯で書けませんでした。 というパターンではありません。

回答No.2

キューを構成する場合、head や tail が、 どこをさしているのか、 ・今読む(書く)ところ、 ・次に読む(書く)ところ、 を明確にするのが大切です、 さて、投稿されたソースでは、おそらく、 head = tail = 0 として初期化されているのでは ないかと想像しますが、その場合、UartSendBuf[0] が(初回のみ)デッドスペースになっています。 ・最初にデータを押し込む  tail を +1 して、そこに押し込む  (つまり、UartSendBuf[1] に押し込む) ・最初に引き出すデータ head を +1 したところのデータ  (つまり、UartSendBuf[1] を送信) しかも、キュー・フルの条件判断が、 tail を + 1 した結果が、head と一致 なので、 UartSendBuf[1] ~ UartSendBuf[23] まで データが埋まった時点(ここまで、23個) で、tail + 1 が、(巡回させているので) 0 になり、この時点の head と一致します。 これで、UartSendBuf[0] はまだがら空きな のに、キュー・フルになってしまいます。 (24個目のデータは捨てられます) こういう事ですね。 キュー・フルの判断は、 「データを書こうとしているところに既に データがある」ですから、 head, tail が、「これからデータを書こ うと/読もうと」するところであれば、 void enqueue(BYTE x) { if(Tail==Head){ QueueState = QSTATE_FULL; return; } UartSendBuf[Tail]=x; Tail=(Tail+1)%MAX_BUF_NUM; /*添え字の循環*/ return; } BYTE dequeue(void) { BYTE x; if(Head==Tail){ QueueState = QSTATE_EMPTY; return; } x=UartSendBuf[Head]; Head=(Head+1)%MAX_BUF_NUM; return x; } です。 (それぞれ、+1 している位置に注意) あと、 UartSendBuf[Head]=0x00; //出力し終わったら0x00でクリアしておく。 は、不要です。(そのデータは次に上書きされるまで使われることはないから) さらに、キュー・フルを、ヘッダの位置関係と、 キューの数の両方で判断していますが、これは、 無駄です。 というか、設計的にうまくいかなくて、キューの数 でも判断しようかという、小手先の判断ではないか という気がします。 本来、どちらか一方だけで判断できるはずなので、 それができないと言うことは、「どこか間違っている」 のです。

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

要は「キューが空の状態」と「キューがいっぱいの状態」を区別できればいいだけなので, キューに「データがあるかないか」という情報を付加する手もあります. この辺は「プログラムの見やすさ」とか「実際の動作速度」とかの兼ね合いもある.

関連するQ&A