引き続き、mbedとmpu-6050のコンビと戯れています。
今回は
連続してmpu6050からデータ取得およびターミナル出力を繰り返していると一定時間経過して止まってしまう問題に取り組みました。
一応の解決をしました。
■実験向けにマイコンとセンサを固定
とりあえず箱型にして、90度ずつの姿勢をとれるようにした図。
■問題の起きている状況
mpu-6050_testのプロジェクトを展開して実験を進めています。
whileループ内で
----
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
----
と
----
pc.printf("t %d,ax %.3f;ay %.3f;az %.3f;gx %.3f;gy %.3f;gz%.3f,\n\r"
,TimeMs,(float)(ax)/16384,(float)(ay)/16384,(float)(az)/16384
,(float)(gx)/131,(float)(gy)/131,(float)(gz)/131);
----
をただただ回すだけです。
これをやると、1130回出力すると止まります。
何が止まっているのか分かりません。PCとの通信が途切れているかもしれません。
しかし、getMotion6を2文重ねると565回で止まります。
つまり、mpu6050との通信の回数に限界があるような気がします。
よくあるパターンとして、何かのカウンタとかがオーバーフローしてしまうとかが考えられます。
ハードウェア的な問題であれば毎回同じ回数で止まるっていうのは考えにくいです。
■問題のあるところ
getMotion6を実行することに問題があるのでそれの中身を、mpu6050.cpp→i2cdev.cppという様に順を追って確認します。
そこでなにか回数を重ねたら変化しそうなものは無いかを探ります。
mpu6050.cpp内では
----
void MPU6050::getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz)
{
i2Cdev.readBytesM6_2(devAddr, MPU6050_RA_ACCEL_XOUT_H, 14, buffer);
*ax = (((int16_t)buffer[0]) << 8) | buffer[1];
*ay = (((int16_t)buffer[2]) << 8) | buffer[3];
*az = (((int16_t)buffer[4]) << 8) | buffer[5];
*gx = (((int16_t)buffer[8]) << 8) | buffer[9];
*gy = (((int16_t)buffer[10]) << 8) | buffer[11];
*gz = (((int16_t)buffer[12]) << 8) | buffer[13];
}
----
としか書かれていません。
i2cdev.cppでは
----
int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout)
{
char command[1];
command[0] = regAddr;
char *redData = (char*)malloc(length);
i2c.write(devAddr<<1, command, 1, true);
i2c.read(devAddr<<1, redData, length);
for(int i =0; i < length; i++) {
data[i] = redData[i];
}
return length;
}
----
と書かれています。
グローバル変数とかは利用されていません。
ここで一番臭いのはmallocです。
動的なメモリなんとかだったと思いますが(プログラムとかにわかだしわからん。)
あらかじめ決められた数の配列とかではなくて、状況におおじて使うメモリの大きさを変更するためにプログラム内でメモリを確保する関数です。
lengthの大きさを使ってますね。
mpu6050.cppでは14を入れています。
まーつまり、センサとかから帰ってくる値の大きさによって受け取る数を調整しているんだと思われます。
ちなみにmallocは確保したメモリをずっと確保しているので、毎回使うと次々にメモリを使っていきます。(大学3年の時にハマった経験があったので…しっている)
なので確保した奴を要らなくなったら開放してやる必要があります。
そこらへんはmallocでググればわかると思います。
■解決策
にわかな僕はいくつか解決策を考えました。
・getMotion6専用の関数としてredData[14]を書いちゃう
----
int8_t I2Cdev::readBytesM6(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout)
{
char command[1];
command[0] = regAddr;
char redData[14];
i2c.write(devAddr<<1, command, 1, true);
i2c.read(devAddr<<1, redData, length);
for(int i =0; i < length; i++) {
data[i] = redData[i];
}
return length;
}
----
・mallocを使わない
----
int8_t I2Cdev::readBytesM6_(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout)
{
char command[1];
command[0] = regAddr;
char redData[length];
i2c.write(devAddr<<1, command, 1, true);
i2c.read(devAddr<<1, redData, length);
for(int i =0; i < length; i++) {
data[i] = redData[i];
}
return length;
}
----
・freeをつかって開放する
----
int8_t I2Cdev::readBytesM6_2(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout)
{
char command[1];
command[0] = regAddr;
char *redData = (char*)malloc(length);
i2c.write(devAddr<<1, command, 1, true);
i2c.read(devAddr<<1, redData, length);
for(int i =0; i < length; i++) {
data[i] = redData[i];
}
free(redData);
return length;
}
----
どれもすべて上手くいきました。
ではなぜ、そもそもmallocだったのか・・・わかりません。
重要な何かを見逃しているかもしれませんし、
にわかにわからないようなスマートなやり方があるのかもしれません。
分かる人は教えてください。
■オマケ
タイマー割り込みを使う。
こちらのサイト様をそのまま使わせていただきます(http://www.wsnak.com/wsnakblog/?p=3449)
1秒Lチカを腕時計で確認しました。細かい誤差とかは分かりません。
----
// インターバルタイマ 割込みハンドラ
void isrInterval(void) {
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
TimeMs++;
}
----
TimeMsとかグローバルで宣言して時間をはかるのをいつもやります。
そんなものはそもそも用意されているのだと思いますが・・・。
といって割り込をつかったデータを取ってエクセルで表示してみてフィルタのちょうしとかためす予定だったんですが・・・
explore落ちたのでもうやる気ないです。(よくブログ更新した、おれえらい。)
[2回]
PR