- ベストアンサー
C18(PIC18)でのシフト演算など
- PIC18で、加速度センサからの値を読み取るプログラムのif文の不具合を解消したい
- 加速度センサとはHM55Bというもので、詳細はリンクを参照してください
- プログラムの不具合原因を見つけることができず、アドバイスを求めています
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
楽できる関数を忘れてました。 atan2ですべて簡単に解決できます! 使っているコンパイラで利用できるか確認してみてください。 atan2でだめなら、元々の方位センサの値を疑ったほうが良さそうですよ。 で、その他の問題点。 問題点1. itoa((int)(dx*10000.f), com_xString); ← 10000倍するとintの有効桁を超えます! 問題点2. if((float)dx == 0 && (float)dy == 0){ ← floatに型変換する必要なし。 問題点3. itoa((int)(teta*10000.f), com_tetaString);← 10000倍するとintの有効桁を超えます!
その他の回答 (8)
- zwi
- ベストアンサー率56% (730/1282)
追記:gccで動作確認してみました。 今のソースコードでは、10000倍している以外には問題がなかったです。 とすると、方位センサの値自体に問題がある可能性があります。 実際の方位とX,Y値をちゃんと検証してみないといけません。 直接X,Y値をチェックしてみてください。
- zwi
- ベストアンサー率56% (730/1282)
今日見てみたら、ラジアン角もあやしいですね。 http://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%B8%E3%82%A2%E3%83%B3 360度=2πラジアンなので、90度づつ補正するなら2πラジアンの1/4,2/4,3/4の値しか出てこないはずです。 C言語のライブラリの三角関数はすべてラジアン角で扱いますので、ちゃんとラジアン角を理解してください。
補足
if文と合わせて、再度検討してみます。 明らかに、結果が0になるところが多いので…
- zwi
- ベストアンサー率56% (730/1282)
まず簡単にするためにきっちりと4つの象限に分けて書きましょう。 後で動いてから省略できるところは省略すればよいです。 それとcomxが0のとき、割り算するのは結果が無限大になってしまうので、if文で切り分けて別処理するか、分子と分母を入れ替えて割り算できる方法を考えましょう。 それと実際のデータを入力せずに45度づつぐらいのデータをダミーで作ってちゃんと角度が計算出来ているかテストしてください。 ラジアン角も理解していて、プログラムの作り方も方向的には合っているので、後は式として正しくすれば問題ないはずです。これから私は外出するので、細かい検証は出来てませんけど。 >itoa((int)teta*100, com_tetaString); intにしてから100倍しても小数部は失われています。100倍してからintにしましょう。 itoa((int)(teta*100.0f), com_tetaString); これ、ラジアン角のままですが度数法に直さなくて良いんですかね?
補足
とりあえず、ラジアンで構わないので、結果を表示したいです。 プログラムの条件式を、以下のように訂正したのですが、 やはり上手く表示できません。ご指摘、お願いします。 (極限、例えばx=0とかのif文は必要ないかと思いますが、一応付けています) --------------------------------------------------------------------- //負の数なら、16bitの補数にする if((dx & (1 << 10)) != 0) dx |= 0xf800; if((dy & (1 << 10)) != 0) dy |= 0xf800; //10進変換 itoa((int)(dx*10000.f), com_xString); itoa((int)(dy*10000.f), com_yString); comx = (float)dx; comy = (float)dy; //teta = asin(comy / sqrt((comy*comy) + (comx*comx))); teta = atan(comy / comx); //例外処理 if((float)dx == 0 && (float)dy == 0){ teta = 9999.0f; } else{ //第1象限 if((float)dx > 0 && (float)dy > 0) teta = teta; //第2象限 if((float)dx < 0 && (float)dy > 0) teta = 3.1415f + teta; //第3象限 if((float)dx < 0 && (float)dy < 0) teta = 3.1415f + teta; //第4象限 if((float)dx > 0 && (float)dy < 0) teta = 2.0f * 3.1415f + teta; //極値0 if((float)dx > 0 && (float)dy == 0) teta = 0.0f; //極値pi/2 if((float)dx == 0 && (float)dy > 0) teta = 3.1415f/2.0f; //極値pi if((float)dx < 0 && (float)dy == 0) teta = 3.1415f; //極値3pi/2 if((float)dx == 0 && (float)dy < 0) teta = (3.0f*3.1415f)/2.0f; } itoa((int)(teta*10000.f), com_tetaString); ------------------------------------------------------------------
- zwi
- ベストアンサー率56% (730/1282)
浮動小数点で参考になりそうなページ。 http://www.cc.kyoto-su.ac.jp/~yamada/programming/float.html それとのPIC用のC言語(いくつかメーカーがあり仕様が各々違います)で、floatとdoubleが同じに精度の型として扱われているコンパイラもあるようですので確認してください。 参考↓ http://www.ee.fukui-nct.ac.jp/~yoneda/text/other/C/A_07.htm
- zwi
- ベストアンサー率56% (730/1282)
atan関数は、float型の浮動小数点でアークタンジェントの計算を行う関数で、引き数として与えられるのはfloat型の変数だけです。 文字列の10進数は人間のために必要なだけで計算に使ってはいけません。表示にだけ使用してください。CPUで文字列10進数で計算を行うことは絶対ありません! int型からfloat型に変換するにはキャストを行う必要があります。 int a; float f; a = 10; f= (float)a; ← 2進から浮動小数点に変換されて代入されます。 それと、浮動小数点の数値は、3や6では無く3.0fや6.0fと書くようにしてください。 参考↓浮動小数点の仕様。 http://ja.wikipedia.org/wiki/IEEE_754 使いこなすためには、ちゃんと勉強する必要があります。 C言語の浮動小数点について本を読むかWEBサイトで勉強してください。 (注意)前にも書きましたが浮動小数点は処理が重く、プログラムメモリも大量に消費します。プログラムサイズと処理時間には十分に注意してください。PICはソフトウェアで浮動小数点を計算しています。x86系のCPUはCPU自身のハードで浮動小数点を計算できるので高速でコンパクトになります。
補足
プログラムを以下のように改良したのですが、 どうも、tetaの計算が上手くいきません。 条件文もこれでいいのか、自信がありません。 ------------------------------------------------------------------ float comx, comy; comx = (float)dx; comy = (float)dy; if(dy < 0) teta = atan(-comy / comx); if(dx > 0 && dy > 0 || dx < 0) teta = atan(-comy / comx) + 3.1415f; if(dx < 0 && dy < 0) teta = atan(-comy / comx) + 2.0f*3.1415f; itoa((int)teta*100, com_tetaString); ------------------------------------------------------------------- よろしくお願いします。
- zwi
- ベストアンサー率56% (730/1282)
>if((dx & (1 << 10)) != 0) dx |= negbits; >if((dy & (1 << 10)) != 0) dy |= negbits; negbitsが0xf800なら問題ないと思います。これで-1024~1023までの値になります。 見てみましたが、uitoaはちゃんとしている様に見えます。 65530なのは、unsignedとして表示しているためで、signedとして処理すれば-6という値です。signedとして扱うにはitoa()を使えば良いと思います。使えないんでしたっけ? 文字化けするほうは、バッファの渡しの間違いか別の原因の気が。補足で書いてもらったソースには原因がなさそうなので。 >また、2進数から10進数(角度の計算をしたい)にするには、どうすればよいでしょうか? 基本的にCPUの中では2進法で計算します。WindowsPCだろうとPICだろうと同じことです。intは、2進法で計算するための型で符号付16ビットの値を計算に使うことができます。 で計算したいのでしたら、普通に加減除乗算をしてください。DX*=5;と書けば5倍されますし、2進法であることを意識するのは、32768を超えたりする値のときだけです。 もし、小数点以下を扱いたいという意味で聞いたのであれば、floatなどの浮動小数点が使えるとは思いますが、かなり重い処理なので、覚悟して使ってください。それとライブラリも大きなメモリサイズのものがリンクされます。メモリのサイズにも注意してください。 表示もsprintfなどを使わないと、自前で浮動小数点を10進法で表示させることは高度すぎて無理です。 通常、PICの貧弱なCPUでは整数のまま角度計算できるように工夫するのが普通です。少数以下がほしいときも10倍して計算するとか工夫すれば何とかできるものです。
補足
文字化けは、単純なタイプミスでした。 itoa()が使えたので、正しく表示することもできました。 2進で計算する理由は分かるのですが、 今、角度(arctan)の計算をしないといけないので、 どうしても10進で計算したいのですが、動いてくれません。 PICの処理能力以前に、プログラムに問題があると思うのですが、 どこが問題かが分かりません。ご指摘お願いします。 ------------------------------------------------------------------- if((WORD)val == 0xc){ sendCmnd(2); //READコマンド送信 dx = sendCmnd(3); //11bitデータ呼び出し dy = sendCmnd(3); //11bitデータ呼び出し PORTBbits.RB3 = 1; //CSB = high } Delay10TCYx(10); //待ち時間 PORTBbits.RB3 = 0; //CSB = low sendCmnd(0); //resetコマンド送信 PORTBbits.RB3 = 1; //CSB = high TRISBbits.TRISB1 = 1; TRISBbits.TRISB2 = 1; TRISBbits.TRISB3 = 1; //負の数なら、16bitの補数にする if((dx & (1 << 10)) != 0) dx |= 0xf800; if((dy & (1 << 10)) != 0) dy |= 0xf800; //10進変換 itoa(dx, com_xString); itoa(dy, com_yString); if(com_yString < 0) teta = atan((-com_yString) / com_xString); if(com_xString > 0 && com_yString > 0 || com_xString < 0) teta = atan((-com_yString) / com_xString) + 3; if(com_xString < 0 && com_xString < 0) teta = atan((-com_yString) / com_xString) + 6; itoa(teta, com_tetaString); -------------------------------------------------------------------
- zwi
- ベストアンサー率56% (730/1282)
情報不足ですが、今わかるところだけ。 加速度センサーの制御フローと違っているように見えます。 http://www.hitachi-metals.co.jp/prod/prod01/p01_13_a1.htm#07 1100は送信するコマンドで、READを意味するコマンドです。 で、加速度の計測が終わって帰って来るビット列が11になるまでループで待ち続ける必要があると制御フローには書かれています。確認をしてみてください。 ちなみに、0x0cか12が1100というビット列なら正しい比較値です。
補足
アドバイスありがとうございます。 質問投稿後、再度確認したところ、接続不良だったようで、 上のプログラムのif文が「0xc」で反応しました。 計測終了はOKなのですが、次のステップでまたつまずいてしまいました。 下のプログラムで、dxとdyの値がうまくとれません。 dxの方は、「65530」ぐらいが表示され、 dyの方は、完全に文字化けしてしまいました。 ちなみに、表示に使っている関数(uitoa)では、 例えば条件式の「0xc」を引数にすると、「12」と表示されます。 補数の計算をしたいので、2進数で値を得たいのですが、どうすればよいでしょうか? また、2進数から10進数(角度の計算をしたい)にするには、どうすればよいでしょうか? ------------------------------------------------------------------- if((WORD)val1 == 0xc){ //例のif文 sendCmnd(2); //READコマンド送信 おそらくOK dx = sendCmnd(3); //11bitデータ呼び出し dy = sendCmnd(3); //11bitデータ呼び出し PORTBbits.RB3 = 1; //CSB = high } //測定終了時のリセット Delay10TCYx(10); //待ち時間 PORTBbits.RB3 = 0; //CSB = low val1 = sendCmnd(0); //resetコマンド送信 PORTBbits.RB3 = 1; //CSB = high TRISBbits.TRISB1 = 1; TRISBbits.TRISB2 = 1; TRISBbits.TRISB3 = 1; //負の数なら、16bitの補数にする if((dx & (1 << 10)) != 0) dx |= negbits; if((dy & (1 << 10)) != 0) dy |= negbits; ------------------------------------------------------------------- 文字表示用 ------------------------------------------------------------------- void uitoa(WORD Value, BYTE *Buffer) { BYTE i; WORD Digit; //桁 WORD Divisor; //除数 BOOL Printed = FALSE; if(Value) { for(i = 0, Divisor = 10000; i < 5; i++) { Digit = Value/Divisor; if(Digit || Printed) { *Buffer++ = '0' + Digit; Value -= Digit*Divisor; Printed = TRUE; } Divisor /= 10; } } else { *Buffer++ = '0'; } *Buffer = '\0'; } //#endif ------------------------------------------------------------------- よろしくお願いします。
リンク先には HM55B は「方位センサ」と書かれてますが、 その為に期待する値が来ないのでは?
補足
入力ミスでした。 「加速度センサ」は「方位センサ」の打ち間違いです。
お礼
以下のプログラムで正しい方角のteta(単位:度)が表示されました。 計測の為の待ち時間(60ms)が不足していたことも原因でした。 長い間、ありがとうございました。 ------------------------------------------------------------------- //方位センサー val = 0; TRISBbits.TRISB1 = 0; TRISBbits.TRISB2 = 0; TRISBbits.TRISB3 = 0; TRISBbits.TRISB4 = 1; //RB4を入力モード sendCmnd(0); //resetコマンド送信 sendCmnd(1); //測定開始コマンド送信 Delay1KTCYx(240); //計測終了に60msの待ち時間が必要 ひとつで約23ms Delay1KTCYx(240); Delay1KTCYx(240); val = sendCmnd(2); //READコマンド送信 if((WORD)val == 0xc){ sendCmnd(2); //READコマンド送信 dx = sendCmnd(3); //11bitデータ呼び出し dy = sendCmnd(3); //11bitデータ呼び出し PORTBbits.RB3 = 1; //CSB = high } Delay10TCYx(10); //待ち時間 PORTBbits.RB3 = 0; //CSB = low sendCmnd(0); //resetコマンド送信 PORTBbits.RB3 = 1; //CSB = high TRISBbits.TRISB1 = 1; TRISBbits.TRISB2 = 1; TRISBbits.TRISB3 = 1; //負の数なら、16bitの補数にする if((dx & (1 << 10)) != 0) dx |= 0xf800; if((dy & (1 << 10)) != 0) dy |= 0xf800; //comxとcomyは実数型 comx = (float)dx; comy = (float)dy; //10進表示 degに変換 itoa((int)dx, com_xString); itoa((int)dy, com_yString); //角度(方位)の計算 teta = atan2(comy, comx); //teta10進表示 deg変換 teta = (teta*180.0f)/3.1415f; itoa((int)teta, com_tetaString); -------------------------------------------------------------------