fc2ブログ

773mbar

2024年04月 ≪  12345678910111213141516171819202122232425262728293031 ≫ 2024年06月

Intel Galileo : I/Oの初期化の問題

GalileoのI/O関連でうまく動作しないトラブルが発生。

その直接の原因ではないが、調べていて通常のArduinoとは違う注意すべき点があることに気付いた。
I/Oの初期設定のタイミングだ。

例えば他のBlogとかで、LiquidCrystalライブラリを使ったスケッチがそのままでは動作しない、という記事がある。
例えばこんなかんじ。

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
    lcd.init(1, 12, 255, 11, 5, 4, 3, 2, 0, 0, 0, 0);
    (以下略)

このようにsetup()の中でLiquidCrystalクラスのinitを呼ばなければならない。

この原因は使用するライブラリのクラスのコンストラクタと、main()関数にあるようだ。
Galileoのmain関数の中ではinit関数が呼ばれ、その中でpinInit関数が呼ばれる。
GalileoのI/O端子はI/Oエキスパンダの後にマルチプレクサのICが接続されている。例えばpinMode関数を呼べば、I/O関連の設定とともにマルチプレクサが切り替えられるようだ。しかしpinInit関数が呼ばれていないとマルチプレクサが正しく切り替わらないようだ(詳細未確認)。

そして、例えば上記LiquidCrystalのようにグローバルなインスタンスを置いた場合、そのインスタンスの初期化はmain関数の実行前に行われる。つまりそのクラスのコンストラクタはmain関数の前、pinInit関数の実行前に実行されることになる。よって、コンストラクタ関数内に例えばpinMode()関数がある場合はそれが正常に動作しないのかもしれない。
残念ながら、main関数に入った後にグローバルに置かれたインスタンスのコンストラクタを実行させる事は基本的には不可能。LiquidCrystalクラスはコンストラクタの中からinit関数を呼び、init関数内でpinMode等を実行しているので、setup()の中(つまりmain()の中)でもう一度initを実行することで正しく設定できるようになっているようだ。

という事で、もし他のスケッチ等のライブラリでもコンストラクタ内で直接pinMode等を実行しているようなものであれば厄介。コンストラクタは各種パラメーターの保存にとどめ、I/O関連の設定は別の(publicな)メソッドで実行するようにライブラリを書き換えたほうがいいだろう。

Intel Galileo | Comments(0) | Trackbacks(-)

Intel Galileo : マルチスレッドを試す

Galileoのスケッチ、子プロセスも起動できたし、マルチスレッドも動くはず。

やってみよう。

int led = 13;
unsigned int n = 0;

void setup() {
  pthread_t id_thread1, id_thread2;
  // ふたつのスレッドを起動する
  pthread_create(&id_thread1, NULL, thread1, NULL);
  pthread_create(&id_thread2, NULL, thread2, NULL);
}

void loop() {
  // 主スレッド、なにもしない。
}

void *thread1(void *arg) {
  pinMode(led, OUTPUT);
  while(true) loop_t1();
}

void *thread2(void *arg) {
  Serial.begin(115200);
  while(true) loop_t2();
}

void loop_t1() {
  digitalWrite(led, HIGH); 
  delay(1000); 
  digitalWrite(led, LOW); 
  delay(1000);
}

void loop_t2() {
  Serial.println(n++);
  delay(1000);
}

わざわざ while(true) loop_t1(); なんて書く必用は無いのだけど、Arduinoならではの setup() / loop() のスタイルに近いほうがそれっぽいなぁ、と思って。
スレッドの起動後は本来なら detach とか join とか必用だけど、いずれのスレッドも無限ループで終わりが無いので省略という行儀悪い書き方。

ということで、あっさり動いた。
これで、シリアルモニタに数をカウントしながらLEDが点滅する。
何か処理しつつ、センサーの入力待ちとかしたい時には積極的にマルチスレッドを使うというのも便利かもしれない。

Intel Galileo | Comments(-) | Trackbacks(-)

Intel Galileo : スケッチ内で子プロセスを動かす

Galileoのスケッチはプロセスだから、子プロセスも動かせるはず。
ってことは、まるで複数の loop() を同時に動かすようなスケッチも書ける。
ってことで書いてみる。

int led = 13;
unsigned int n = 0;

void setup() {
  if(fork()) {
    // 親プロセス
    pinMode(led, OUTPUT);
    while(true) loop_parent();
  } else {
    // 子プロセス
    Serial.begin(115200);
    while(true) loop_child();
  }
}

void loop() {
  // dummy
}

// 親プロセスのloop
void loop_parent() {
  digitalWrite(led, HIGH); 
  delay(1000); 
  digitalWrite(led, LOW); 
  delay(1000);
}

// 子プロセスのloop
void loop_child() {
  Serial.println(n++);
  delay(1000);
}

これで、シリアルモニタに数をカウントしつつLEDが点滅する。
やっていることはLinux(UNIX)では当たり前の事なんだけど、なんだかLinux上で普通にCで何か書くよりもArduinoのIDE上で何か書くほうがかなり「簡単」って雰囲気が感じられる。
まぁ、実際、例えば上記ソースを普通のCで書くとしたら#include <unistd.h> とか必須だし、loop_parent()等のプロトタイプ宣言も必用になる。こういった細かい事を気にしないでいいのがArduinoのプログラミングのいい点のひとつだと思う。そしてそんなお気軽・簡単でありながら、普通にLinuxのプログラミングができてしまうのだから、Linuxのプログラミングの入門としてもGalileoは良いかもしれない。
と思った。


Intel Galileo | Comments(-) | Trackbacks(-)

Intel Galileoで複数のスケッチを動かしてみる

GalileoのスケッチはLinuxのプロセス、ってことなら、普通に複数動くはず。

やってみよう。

まず定番、BlinkをコンパイルしてGalileoにアップロード。LEDが点滅することを確認する。
その後SSH等でログインし、/sketch/sketch.elf (アップロードしたスケッチそのもの)をコピーする。

root@clanton:~# cd /sketch
root@clanton:/sketch# cp sketch.elf blink

この状態で別のスケッチ(ただしDigital13番にアクセスしないもの)をコンパイルしてGalileoにアップロードする。
そのスケッチが動く事を確認。
そしてこの状態で、先ほどコピーした blink のスケッチを動かす。

root@clanton:/sketch# ./blink /dev/null /dev/null &

スケッチの起動にはパラメータを3個まで並べることができ、それぞれSerial, Serial1, Serial2 に割り当てるシリアルデバイス(例えば/dev/ttyGS0)を指定する。ただし最低2個指定する必用がある。この例のBlinkではSerialを使用していないので、/dev/nullを指定した。

これで元のスケッチが動いたままBlinkも動き、LEDが点滅する。
Intel Galileo | Comments(-) | Trackbacks(-)

Intel Galileoでprintfを使う

Galileoのスケッチは他のarduinoとは違ってLinuxのひとつのプロセスとして動く。

ってことは当然の事ながら、printfとかで標準出力に何か出す事もできる。

ところが、main()関数で、stdoutとstderrは、それぞれ /tmp/log.txt と /tmp/log_er.txt に出力するよう設定されている。デバッグ時にはこれが簡単に確認できたら便利かもしれない。

ってことで、コンソールに設定してみる。

void setup() {
  stdout = freopen("/dev/console", "w", stdout);
  stderr = freopen("/dev/console", "w", stderr);

  printf("TEST\n");
}

これでコンソールに「TEST」と出力される。

当たり前だけど便利かも。

arduinoだったらSerial.print()を同様なデバッグに使うことができるが、fprintfに相当する機能を使いたかったらsprintfで一旦バッファに書き出す必用があった。しかしGalileoならfprintfが使える。

シリアルを接続していないのであれば、上記/dev/console の部分を /dev/ttyGS0 にすれば、IDEのシリアルモニタに出力される。当たり前だけど便利。


Intel Galileo | Comments(-) | Trackbacks(-)