fc2ブログ

773mbar

2013年12月 ≪  12345678910111213141516171819202122232425262728293031 ≫ 2014年02月
TOP ≫ ARCHIVE ≫ 2014年01月
ARCHIVE ≫ 2014年01月
      
≪ 前月 |  2014年01月  | 翌月 ≫

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(-)

Intel Galileo : もしACアダプターを間違えたら…

Galileoは外部電源必須、しかも5V、これが内径2.1mmのジャック。
対して他のArduinoは同じ内径2.1mmのジャックで7〜12V。
いつか間違えて9Vとか12VとかのACアダプターを接続してしまいそう。
という事で、仮にACアダプターを間違えて(例えば9Vを接続して)しまった場合にどのようなダメージが想定されるか、回路図から確認してみた。

まず意外だったのが、この5V入力を直接使っている部品はそれほど多くない。
まずはUSBホスト端子のVCC端子に流す電流を制限するためのICであるTPS2051BDBVR、これの許容電圧が最大6V。これが破損する可能性はあるだろう。ただ、これが壊れてもUSBホスト端子に電源が供給されなくなるだけでGalileo自体が動作しなくなってしまうわけではない。

もうひとつがDC-DCコンバーターであるTPS652510。このICで5Vから内部動作に必用な3.3V、1.5V、1.0Vを作っている。このICの入力は最大16V。つまり9Vや12VのACアダプターを接続してしまったところでこのICが壊れる事は無く正規の電圧を出力してくれる。よってQuarkを含め多くの部品を破損させてしまうような事はなさそうだ。

問題はI/O関連。
IOREFのジャンパーを5V側に接続すると(つまり出荷時状態)、ACアダプターの電源が、I/OエキスパンダーのCY8C9540A、マルチプレクサとして使用しているアナログスイッチのTS5A23159、電圧レベルシフタTXS0108Eに接続される。これらは5V想定なので許容電圧をオーバーし、破損する可能性がある。また、シールドへの電源もACアダプターの電源がそのまま供給されることになる。
対してIOREFのジャンパーが3.3V側であれば、これらのICやシールドへの電源供給はTPS652510によって作られた3.3Vになる。
つまり、もし5V対応のシールドを接続しないのであれば、IOREFのジャンパーを3.3V側に設定しておいたほうが、ACアダプターを間違えて接続した場合のダメージを低く抑えることができるということだ(※)。

さらに念のため、簡単な保護回路を付けてみた。
ACアダプターの入力に6.2Vのツェナーダイオードを接続した。



まぁ、気休めではあるけれど…
ツェナーダイオードの定格は1W、ACアダプターはそれ以上の供給能力があるのでツェナーダイオードは破損するだろうが、ツェナーダイオードの破損モードはショートが多いようなのでそれに期待、ACアダプター出力が過電流になり保護回路が動作する…という期待だ。うまくツェナーダイオードが壊れてショートしてくれればいいが。ちなみに使用しているACアダプターの出力がショートすると保護回路が働く事は確認済み。…つまりショートさせてしまったことがあるという事だ(笑)。

※ちなみに日経Linux 2014年2月号の記事には「通常は、一般的な5VのAVアダプターを利用する設定」「3.3V(に設定した)の状態で5VのACアダプターを接続するとボードが破損します」という記載があるが大きな誤り。このジャンパーはあくまでもシールドの入出力電圧を設定するものであって電源入力を設定するものではなく、ジャンパー設定に関わらず5VのACアダプターを接続する必用がある。ボード内部のDC-DCコンバーターであるTPS652510の入力電圧は最低4.5Vであり、仮に3.3VのACアダプターを接続したら動作しないだろう。
この記事には他に、USBを接続してから電源を接続する(正しくは電源を接続してからUSBを接続する)、シリアル端子の出力レベルは3.3V (正しくはMAX3232の出力であり±5Vを越える)と、機器を壊しかねない危険な間違いがあり注意が必用。
Intel Galileo | Comments(-) | Trackbacks(-)

Galileoのmillis()

Galileoのmillos()関数の値がおかしい。
調べてみたら、こんな実装。
ソースは Java/hardware/arduino/x86/cores/arduino/UtilTime.cpp

unsigned long millis( void )
{
    return micros() / 1000;
}

これでは4294967(2^32/1000)、つまり約1時間11分でループしてしまう。
そこで以下のように書き換え。

unsigned long millis( void )
{
  struct timespec t;
  t.tv_sec = t.tv_nsec = 0;
  clock_gettime(CLOCK_REALTIME, &t);
  return (unsigned long)(t.tv_sec)*1000L + t.tv_nsec / 1000000L ;
}

これで32bitフルにカウントされた値が返るようになる。
ちなみにmicros()はこんな実装。それをちょこっと変更しただけ。

unsigned long micros( void )
{
  struct timespec t;
  t.tv_sec = t.tv_nsec = 0;
  /* Considering the system does not suspend CLOCK_REALTIME is welcome.
     However, if in the future our system suspend, we need to replace
     CLOCK_REALTIME by CLOCK_BOOTTIME and apply the proper patches in 
     the kernel. */ 
  clock_gettime(CLOCK_REALTIME, &t);
  return (unsigned long)(t.tv_sec)*1000000L + t.tv_nsec / 1000L ;
}
Intel Galileo | Comments(-) | Trackbacks(-)

Galileoのシリアル接続

Intel Galileoが国内にも入り始め、様々なBlogでもGalileoに関する情報が増え始めた。
ただ、その中でもLinuxにアクセスする際、シリアル(RS232)ではなくネットワーク経由がほとんど。やはり専用のインターフェースケーブルが現時点にて無いためか、シリアル接続での確認は後回し、という記事が多い。

しかし言おう。シリアル接続は絶対あったほうがいい。

スクリーンショット 2014-01-25

シリアル接続は、いわゆるコンソール。Linuxを使っている人には説明不要だと思うが、起動時の様々な情報や、USB端子に何か接続された時とか、スケッチがアップロードされた時とか、様々な情報が得られる。だから、例えばUSBとか接続しても無反応だった時とか、トラブル要因の確認がやりやすい。
ちなみに上記画面はmicroSDで起動したところ。ネットワーク接続はDHCPだけど、IPアドレスが192.168.0.52であることとか、書き込み済みのスケッチが起動したとか、容易に確認できる。

また、内蔵Flashのアップデートに失敗した時には復旧させるためにはシリアル接続が必須となる。

ただ、厄介なのはインターフェースがRS232という事だろう。今時RS232Cインターフェースを搭載したPCなんてほとんど無いのでUSB経由となる。Arduino等ではシリアル接続と言えばFTDIの6ピン端子がデファクトスタンダードだが、もちろんこの信号をGalileoに直接接続することはできない。RS232Cは負論理だから最低でもインバーターが必用になる。

現実的にはUSB-RS232C端子の変換ケーブルを使い、RS232C端子と3.5mmジャックとの変換ケーブルを自作する事になるだろうか。できるならばやってみることをお勧めする。

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