Armadilloフォーラム

Armadillo-460 処理時間の向上について

ito-keiichi

2014年5月15日 20時08分

丸大機工の伊東です。

Armadillo-460のUSBポートを利用して、USBディスプレイアダプタを経由し、VGAモニタ出力させ、6.5インチモニターにアナログデータをグラフ表示させています。
CPUボード(460)とアナログボード(MPC104-ISOADC12)はPC/104接続しています。

以下の仕様を正しく行いたいのですが、上手くいかずに困っています。
アドバイスをお願い致します。

100msのタイマー割込みを使用して、アナログデータのサンプリングを行います。
画面表示には、IOCTLのシステムコマンドを使用して表示させます。
この表示は時計などの時刻を表示する為に1s間隔に行います。

今回問題となっているのは、IOCTLのシステムコマンドの処理時間が100msを超えてしまい、データサンプリングが定期的に行えない事です。
Linux-OSのQTを使用してC言語にて制御プログラムを作成しました。
IOCTLシステムコマンドの処理時間を計測する為に、命令後の前には計測スタート、命令後の後には計測停止を入れ、命令は、IOCTL(xxxx)の1ステップを計測した所、120~300msになりました。
これを解決するには、ソフト的に出来るのでしょうか?
それとも、ハードウェアの例えばクロックを400MHzを高速なクロックへ変更すると、システムコマンドが高速で処理できて可能でしょうか?
それともこれらを解決できる代替製品などがあるのでしょうか?

宜しくお願いします。

コメント

Armadillo-800シリーズ であれば、800MHz の CPU が搭載されています。
ですので、Armadillo-400シリーズよりは高速な処理が可能です。

しかし、ioctl()が原因であれば、CPUが高速になっても ioctl() を呼び出した
ときに、待たされる場合があります。具体的になんの ioctl()をお使いなのか
教えて頂ければ、いくつかアドバイスさせて頂くことが可能かと思います。

また、ioctl() で待たされることによって、他の部分が遅くなるようであれば、
プログラムを複数のスレッドに分けることで改善する場合もございます。こち
らも、どのようなプログラムなのか教えて頂ければ、アドバイスさせて頂きま
す。

お世話になっております。伊東です。

早速のご回答ありがとうございます。

現行の問題となっているプログラムは以下の通りです。
 イニシャルにてファイル記述子を取得します。
gint_sfd = open("/dev/fb2", O_RDWR); //画面のデバイスのファイル記述子を得る

 1秒毎のタイマ割込みにて下記のプログラムを起動します。
void MainWindow::sltdispClock()
{
struct dloarea disppara;

disppara.x = 0;
disppara.y = 0;
disppara.w = 1280;
disppara.h = 1024;
disppara.x2 = 0;
disppara.y2 = 0;

QElapsedTimer et;     // 計測開始
et.start();
if (ioctl(gint_sfd,0xAA,&disppara))
{

}

qint64 t = et.elapsed(); // 処理に掛かったミリ秒を取得
ui->lblNowTime1_2->setText( QString::number(t) ); // ラベルに表示

以上のようにプログラムしています

ご回答の中にスレッドを分割する事で改善する場合もあるようですが、
100msのタイマー割込みを使用してアナログデータをサンプリングして、データ格納します。
それとは別に上記プログラムで記述している様に、1ステップでIOCTLを実行しています。
このIOCTLを動かす際に100ms以上の処理時間が掛かるのですが、
IOCTLの実行中にもタイマ割込みにてデータサンプリングできるという事でしょうか?
それともIOCTLの代わりになる様な命令があるという事なのでしょうか?

ソフトで対応できる様であれば、現行では最良の対応となりますので、宜しくお願い致します。

まず確認させてください。

1) アナログデーターのサンプリング用と fb2への 2つの ioctl() がある
2) アナログデーターのサンプリングは、100ms毎
3) fb2への ioctl()が戻るまでに、100ms以上の時間がかかる
4) fb2 は、udlfbである
5) udlfb の ioctl 0xaa は、DLFB_IOCTL_REPORT_DAMAGE である

ここまで合ってますか? つまり、udlfb 経由で表示している画面のうち、再描
画が必要な場所を取得するのに 100ms以上かかっていると。

damage area の取得をやめて、毎回描画してはどうでしょうか? damage area
を取得するために 100ms もかかるのであれば、あまり嬉しくないような気がし
ます。

Q1) 1280x1024ということですが、全画面描画にはもっと時間がかかりますか?
Q2) 100ms かかっている原因は、どちらでしょうか?
a) a460のUSBスループット
b) displaylink のデバイスが情報を取るため

壊れた描画エリアの「情報」だけを取得する通信なので、「データー量が多く
て a460のスループットが問題になる」ということはないような気がします。そ
うなると、udlfbへの ioctl()は、Armadillo 側の問題ではなく
デバイス自体が時間を使っている、または USB のどこかに遅延を発生させる原
因があることになります。どちらにしてもこの ioctlは、CPU Bound ではない
はずです。

そうなると、この ioctl を別スレッドにしたくなります。Qt には、
QtConcurrent という上位レベルの Thread API があるので、こ
れを使ってはどうでしょうか? QtConcurrent::run()に渡す関数
の中で、fb2への ioctl()を発行するという方法で
す。逆に、データーをサンプリングする方を別スレッドにしても良いかもしれ
ません。サンプルしたデーター自体は Qt の Signal で、スレッドを超えて投
げることが可能です。

しかし、この方法には問題があるかもしれません。

Q3) a460 は、2.6.26 のカーネルを使っているという理解で良いでしょうか?

その場合 fbdev の ioctl() は BKL (big kernel lock) を取る
ため、この ioctl()が戻るまで 他の ioctl()
発行できないかもしれません。つまり、fb2への ioctl() が戻る
まで、データーサンプリングできない可能性があります。

もし BKL を取らないのであれば、前述の通り複数のスレッドに分けることで、
ioctl の遅延を消すことができます。

> その場合 fbdev の ioctl() は BKL (big kernel lock) を取る
> ため、この ioctl()が戻るまで 他の ioctl()
> 発行できないかもしれません。つまり、fb2への ioctl() が戻る
> まで、データーサンプリングできない可能性があります。

あ、間違ってました。タスクスイッチした場合、BKL は開放されますね。ごめ
んなさい。

ですので、「前述の通り複数のスレッドに分けることで、ioctl の遅延を消す
ことができます。」

ご連絡ありがとうございます。
確認事項については、1~5まではその通りです。
damage area の取得をやめて、毎回描画する件ですが、
毎回描画する方法が解らず、当時何かの参考資料を参考にした所、
描画できたのでそのまま使用していた次第です。
Q1,Q2については、具体的には解っていませんが、bのDisplaylinkのように思えます。
Q3は、2.6.26です。
そこで、別スレッドにして解決できるのであれば、
その方法をもう少し具体的にソースコードのご質問をさせて下さい。

現状では、それぞれのタイマで動作する処理をスロットにしています。
タイマは、0.1秒毎と1秒毎で起動するようにしています。
今回、1秒毎に起動するタイマのIOCTL命令のステップが100ms以上かかっているのは解っていますが、
それを別スレッドにする具体的なソースコードの記述を教えて下さい。

中身を省略していますが、下記が0.1秒毎に起動するアナログデータを読み込むスロットです。
(処理時間を計測した所、10ms程度で動作しています。)

/* ************************************** */
// 0.1秒毎に動作するアナログデータ収集 及び 表示
/* ************************************** */
void MainWindow::sltdatalog()
{
int intret;

//アナログデータのデータ読み込み
intret = isoadc12_read();

それと、下記が今回問題となっているスロットです。
(これも中身を省略しています。)

/* ************************************** */
// 1秒毎に動作する時計表示
/* ************************************** */
void MainWindow::sltdispClock()
{
struct dloarea disppara;

disppara.x = 0;
disppara.y = 0;
disppara.w = 1280;
disppara.h = 1024;
disppara.x2 = 0;
disppara.y2 = 0;

if (ioctl(gint_sfd,0xAA,&disppara))
{
}

}

これらは、ソフト起動後に下記のように起動されます。
(省略部分があります。)

/*
* /////////////////////////////////////
* /////////////////////////////////////
*
* メイン画面
*
* /////////////////////////////////////
* /////////////////////////////////////
*/
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{

gint_sfd = open("/dev/fb2", O_RDWR); //画面のデバイスのファイル記述子を得る

//1秒タイマー処理
interval_Clock = new QTimer(this);
connect(interval_Clock, SIGNAL(timeout()), this, SLOT(sltdispClock())); //時計表示用1s
interval_Clock->start(1000); //1秒タイマー:スタート

//0.1秒タイマー処理
interval_Log = new QTimer(this);
connect(interval_Log, SIGNAL(timeout()), this, SLOT(sltdatalog())); //データサンプリング用0.1s
interval_Log->start(100); //データサンプリングタイマースタート

今回 at_yashi さんから教えて頂いた別スレッドとは、
具体的に下記のようにするだけで良いのでしょうか?
実際に実行した所、動作に変化はありませんでした。

/* ************************************** */
// 1秒毎に動作する時計表示
/* ************************************** */
void MainWindow::sltdispClock()
{
// struct dloarea disppara;
//
// disppara.x = 0;
// disppara.y = 0;
// disppara.w = 1280;
// disppara.h = 1024;
// disppara.x2 = 0;
// disppara.y2 = 0;
//
// if (ioctl(gint_sfd,0xAA,&disppara))
// {
// }

QtConcurrent::run(aaaa);
}

void aaaa()
{
struct dloarea disppara;

disppara.x = 0;
disppara.y = 0;
disppara.w = 1280;
disppara.h = 1024;
disppara.x2 = 0;
disppara.y2 = 0;

if (ioctl(gint_sfd,0xAA,&disppara))
{
}
}

今回は、1秒タイマ側でしたが、
優先順位としては、0.1秒側の処理を変更した方が宜しいのではないかと
思っています。

どうか宜しくお願い致します。

追加の内容で、教えて下さい。

下記のプログラムには、先程の内容では省略しましたが、
ui->lblNowDate->setText(localdate);
などのソースコードも含まれています。

/* ************************************** */
// 1秒毎に動作する時計表示
/* ************************************** */
void MainWindow::sltdispClock()
{
struct dloarea disppara;

disppara.x = 0;
disppara.y = 0;
disppara.w = 1280;
disppara.h = 1024;
disppara.x2 = 0;
disppara.y2 = 0;

if (ioctl(gint_sfd,0xAA,&disppara))
{
}

QString localdate = QDate::currentDate().toString("yyyy/MM/dd");
ui->lblNowDate->setText(localdate);

このような場合に、QtConcurrent::runで指定する関数aaaaに
QString localdate = QDate::currentDate().toString("yyyy/MM/dd");
ui->lblNowDate->setText(localdate);
を記述すると、ui がコンパイルエラーとなってしまいます。
関数aaaa にMainWindow の内容をどのようにパラメータで渡したら良いのでしょうか?

それとも別な定義方法があるのでしょうか?

これも併せて教えて下さい。
宜しくお願い致します。

> > 関数aaaa にMainWindow の内容をどのようにパラメータで渡したら良いのでしょうか?
>
> run() に、そのまま渡せば良いはずです。
> http://qt-project.org/doc/qt-5/qtconcurrentrun.html#passing-arguments-t…

ヘッダーで
namespace Ui {
class MainWindow;
}
と定義しています。

そのままとは、run(MainWindow) にするのでしょうか?

これも、もう一度お教え下さい。
宜しくお願い致します。

> そのままとは、run(MainWindow) にするのでしょうか?

aaaa() の signature が、

int aaaa(int a);

であれば、

int i = 0;
run(aaaa, i);

とするという意味です。

aaaa() が、メソッドの場合はさらに注意が必要です。
サンプルを添付しておきます。

ファイル ファイルの説明
a.cpp QtConcurrent::run()のサンプルコード

> 実際に実行した所、動作に変化はありませんでした。

「変化しなかった」とは、「アナログセンサーのサンプリングが、100ms間隔で
行えなかった」ということでしょうか?

void MainWindow::sltdispClock()
{
    QtConcurrent::run(aaaa);
}
 
void aaaa()
{
    struct dloarea disppara;
 
    disppara.x = 0;
    disppara.y = 0;
    disppara.w = 1280;
    disppara.h = 1024;
    disppara.x2 = 0;
    disppara.y2 = 0;
 
    if (ioctl(gint_sfd,0xAA,&disppara))
    {
    }
}

上記のコードであれば、main thread と aaaa は別のスレッドになっているは
ずです。もし気になるようであれば、

qDebug() << QThread::currentThread();

してみると良いです。

ところで、aaaa()ioctl() で得た情報を、
main側に戻す必要はあると思いますが、どうですか?

> 今回は、1秒タイマ側でしたが、
> 優先順位としては、0.1秒側の処理を変更した方が宜しいのではないかと
> 思っています。

それでも良いと思います。ただ、main thread が ioctl() によって止まらない
ようにした方が良いと思います。

Qt の Thread は、以下にサンプルがあるので参考にしてください。
http://qt-project.org/doc/qt-5/examples-threadandconcurrent.html

また、Qtの日本語の書籍にも Thread について記載されいています。

入門 Qt 4プログラミング ISBN978-4-87311-344-9
http://www.oreilly.co.jp/books/9784873113449/
第18章 マルチスレッド

実践 Qt 4プログラミング ISBN978-4-87311-507-8
http://www.oreilly.co.jp/books/9784873115078/
8章 QThreadを使ったマルチスレッドプログラミング

大変おせわになっております。色々とご教示ありがとうございます。

> 「変化しなかった」とは、「アナログセンサーのサンプリングが、100ms間隔で
> 行えなかった」ということでしょうか?

100ms間隔で行えませんでした。
いままでと同様に遅れが出ます。

> 上記のコードであれば、main thread と aaaa は別のスレッドになっているは
> ずです。もし気になるようであれば、
>
>

> qDebug() << QThread::currentThread();
> 

>
> してみると良いです。

モニタに
QThread(0x397670)
が表示されます。

> ところで、aaaa()ioctl() で得た情報を、
> main側に戻す必要はあると思いますが、どうですか?

IOCTLで受けるエリアがグローバル変数の為に、戻さなくてもメイン側で参照できるようになっています。

> それでも良いと思います。ただ、main thread が ioctl() によって止まらない
> ようにした方が良いと思います。

遅れが発生している所から予想すると、
まだ、IOCTLでメインスレッドが止まっているのでしょうか?
他にも遅延解除方法があるのでしょうか?

> Qt の Thread は、以下にサンプルがあるので参考にしてください。
> http://qt-project.org/doc/qt-5/examples-threadandconcurrent.html

こちらも拝見しましたが、英語の文献でなかなか理解までは難しく、
時間がかかりそうです。

以下の書籍は購入したのですが、理解できずにこのフォーラムでお聞きしていました。
すみません。

> また、Qtの日本語の書籍にも Thread について記載されいています。
> 入門 Qt 4プログラミング ISBN978-4-87311-344-9
> http://www.oreilly.co.jp/books/9784873113449/
> 第18章 マルチスレッド
>
> 実践 Qt 4プログラミング ISBN978-4-87311-507-8
> http://www.oreilly.co.jp/books/9784873115078/
> 8章 QThreadを使ったマルチスレッドプログラミング

返信宜しくお願い致します。

> モニタに
> QThread(0x397670)
> が表示されます。

aaaa() の中と slot に指定した関数の中、つまり run() を呼ぶ直前の、両方
でやってみてください。別の thread id が表示されるはずです。別の thread
id が表示されていれば、別スレッドで実行されているはずです。

> > ところで、aaaa()ioctl() で得た情報を、
> > main側に戻す必要はあると思いますが、どうですか?
>
> IOCTLで受けるエリアがグローバル変数の為に、戻さなくてもメイン側で参照できるようになっています。

複数のスレッドから同じ変数を使う場合は、アクセスする区間を mutex などで
ロックした方が良いと思います。

> > それでも良いと思います。ただ、main thread が ioctl() によって止まらない
> > ようにした方が良いと思います。
>
> 遅れが発生している所から予想すると、
> まだ、IOCTLでメインスレッドが止まっているのでしょうか?

ごめんなさい。udlfb への ioctl が遅いという話だったので、そこを別スレッ
ドにする方法を提案してみました。main thread が止る要因って他にあるので
しょうか?

上にも書きましたが、QtConcurrent::run() の前後と、udlfb への ioctl()の
前後で時間を測ってみることはできますか?

お世話になっております。

残念ながら、本日納品のリミットですので、
本機能は実現出来ない為に断念します。

これまで対応ありがとうございました。