読者です 読者をやめる 読者になる 読者になる

bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

XMOS (イベント駆動・並列スレッド)プログラミング(1)

1.ボタンLEDを光らせる

1.1 プログラムを作る

ボタンのLEDを光らせるプログラムは次のようになっていた。

  #include<platform.h>
  out port bled = PORT_BUTTONLED;
  int main() {
    bled <: 0x1;
    while(1)
      ;
    return 0;
  }

このプログラムは、次のようになっている。

2行目の

out port bled = PORT_BUTTONLED;

はポート変数bledを定義定義したもので、ここでは、汎用のポート識別子PORT_BUTTONLEDで初期化される。

XC-5.xnファイルが、PORT_BUTTONLEDをXS1_PORT_4Eにマップする。ここで4Eは、4ビットのピンを表し、4個のボタンLEDに対応する。

ポートはピンとプロセッサの間でのデータの授受に使われる。従って、プロセッサと外部要素とのインタフェースとして機能する。

また、ポートはグローバル変数として宣言されなければならない。

mainの中での最初の命令文

bled <: 0x1;

は、

return 0;

は決して実行されることがない。

1.2 課題

bledの値を0x1から0xFまで変えて、bledとLEDの関係を表で示しなさい。

2.クロックLEDを光らせる。

2.1 クロックLED

クロックLEDとして、赤と緑のLEDが対となったものが12個用意されている。

これらLEDのアノード(陽極)は3個の4ビットポートに接続されている。

それらポートは4A,4B,4Cと名前が付けられており、プログラムの内部では、それらは識別子PORT_CLOCKLED_O/1/2で参照される。(ボタンLEDはポートは4Eで、識別子はPORT_BUTTONLED)。

2.2 緑と赤の選択

緑のLEDのカソード(陰極)は、1ビットポートに接続され、これは1Eの名前が付けられている。

赤も同様で、1Fと名のついたポートを持っている。

これらのポートは、識別子PORT_CLOCKLED_SELG, PORT_CLOCKLED_SELRでプログラムにより参照される。
f:id:bitterharvest:20140721105120j:plain

2.3 問題

次のプログラムはどのLEDを光らせるか考えなさい。

#include <platform.h>
out port cled0 = PORT_CLOCKLED_0;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;
int main(void) {
  cledG <: 0; // disable GREEN line
  cledR <: 1; // enable RED line
  cled0 <: 0x1; // LED pattern
  while (1);
  return 0;
}

2.4 課題

すべての緑色のLEDを光らせるにはどうしたらよいか。プログラムを作成し、実行して検証しなさい。

3.タイマーを使って、LEDを点滅させる

3.1 タイマーの使い方

次はタイマーを用いるときの典型的な命令文である。

timer tmr;
tmr :> t;

tmrと呼ばれるタイマーtmrを定義し、tmrの時刻を変数tに入力する。

次にある一定の時間(FLASH_PERIOD)が経った後に、イベントが生じるようにするためには次のようにする。

t += FLASH_PERIOD;
tmr when timerafter(t) :> void;

まず、tに一定時間加える。timeafter(t)は、tmrがtより大きくなったときイベントが発生し、tmr :> void; が実行される。なお、このとき、tmrの時間を入力するように完結した命令文にする必要があるが、実際には、どこにも代入する必要がないのでvoidが用いられている。

次のプログラムを実行してみる。

#include <platform.h>
#define FLASH_PERIOD 20000000
out port cled0 = PORT_CLOCKLED_0;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;
int main(void) {
  timer tmr;
  unsigned ledGreen = 1;
  unsigned t;
  tmr :> t;
  while (1) {
    cledG <: ledGreen;
    cledR <: !ledGreen;
    cled0 <: 0x1;
    t += FLASH_PERIOD;
    tmr when timerafter(t) :> void;
    ledGreen = !ledGreen;
  }
  return 0;
}

3.2 課題

FLASH_PERIODの時間を徐々に減らしてみる。赤と緑のLEDの色が混ざりオレンジ色となるのを確認すること。

4.LEDを点滅させるとともに走らせる

4.1 説明

複数の入力イベントを有し、イベントが発生したところから、その入力に対する処理を行うプログラムを作成することとする。いま、LEDは、一方で点滅し、他方で、時計方向に回転しているとする。点滅の間隔とLEDを一つ進める間隔は異なっているとし、それぞれの間隔が来たときに点滅、一つ進めるの作業を行うものとする。

この種の作業のために設けられているのが、select文である。この文は、通常

select {
 case
 ...
 breek;
 case
 ...
 break
}

という形をとる。例えば、タイマーtmfFが設定された時間timeFを超えた時にLEDを点滅させ、と、タイマーtmfCが設定された時間timeCを超えた時にLEDを一つ進めるプログラムは、

select {
 case tmrF when timerafter(timeF) :> void;
 // 点滅
 ...
 breek;
 case tmrC when timerafter(timeC) :> void;
 // 一つ進める
 ...
 break
}

4.2 プログラム

プログラムを完成させると次のようになる。

#include <platform.h>
#define FLASH_PERIOD 10000000
#define CYCLE_PERIOD 60000000
out port cled0 = PORT_CLOCKLED_0;
out port cled1 = PORT_CLOCKLED_1;
out port cled2 = PORT_CLOCKLED_2;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;
int main(void) {
  unsigned ledOn = 1;
  unsigned ledVal = 1;
  timer tmrF, tmrC;
  unsigned timeF, timeC;
  tmrF :> timeF;
  tmrC :> timeC;
  while (1) {
    select {
      case tmrF when timerafter(timeF) :&gt; void :
        ledOn = !ledOn;
        cledG <: ledOn;
        timeF += FLASH_PERIOD;
        break;
      case tmrC when timerafter(timeC) :&gt; void :
        cled0 <: ledVal;
        cled1 <: (ledVal >> 4);
        cled2 <: (ledVal >> 8);
        ledVal <<= 1;
        if (ledVal == 0x1000)
          ledVal = 1;
        timeC += CYCLE_PERIOD;
        break;
    }
  }
  return 0;
}

4.3 問題

このプログラムを詳しく説明しなさい。

4.4 課題

反時計回りにするにはどうしたらよいか?プログラムを示しなさい。

5.スレッドによる並列処理(LEDの点滅回転とボタンを押してのスピーカー音)

5.1 説明

2つの処理を並列に行うことを考える。一つはLEDを点滅させながら回転させることであり、他の一つはボタン(一つでも複数でも良い)を押すとスピーカーから音が出てくるものである。このような並列処理は次のようなプログラムで実現される。

par {
cycleLED(cled0, cled1, cled2,
cledG, cledR);
buttonListener(buttons, speaker);
}

cycleLEDのプログラムは、先に出てきたプログラムを修正したものである。

buttonListenerのプログラムは、ボタンが押されたかどうかの判断はwhen pinsneq(0xf)によりなされることを利用して、ボタンが押されている間は、スピーカに断続的な信号を送る様にする。

5.2 プログラム

#include &lt;platform.h&gt;
#define TDELAY 100000
#define TLENGTH 500
void buttonListener(in port b, out port spkr) {
  timer tmr;
  int t, isOn = 1;
  while (1) {
    b when pinsneq(0xf) :> void;
    tmr :> t;
    for (int i=0; i&lt;TLENGTH; i++) {
      isOn = !isOn;
      t += TDELAY;
      tmr when timerafter(t) :> void;
      spkr <: isOn;
    }
  }
}
#define FLASH_PERIOD 10000000
#define CYCLE_PERIOD 60000000
int cycleLED(out port cled0, out port cled1, out port cled2, out port cledG, out port cledR) {
  unsigned ledOn = 1;
  unsigned ledVal = 1;
  timer tmrF, tmrC;
  unsigned timeF, timeC;
  tmrF :> timeF;
  tmrC :> timeC;
  while (1) {
    select {
      case tmrF when timerafter(timeF) :> void :
        ledOn = !ledOn;
        cledG <: ledOn;
        timeF += FLASH_PERIOD;
        break;
      case tmrC when timerafter(timeC) :> void :
        cled0 <: ledVal;
        cled1 <: (ledVal >> 4);
        cled2 <: (ledVal >> 8);
        ledVal <<= 1;
        if (ledVal == 0x1000)
          ledVal = 1;
        timeC += CYCLE_PERIOD;
        break;
    }
  }
  return 0;
}
in port buttons = PORT_BUTTON;
out port speaker = PORT_SPEAKER;
out port cled0 = PORT_CLOCKLED_0;
out port cled1 = PORT_CLOCKLED_1;
out port cled2 = PORT_CLOCKLED_2;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;
int main(void) {
  par {
    cycleLED(cled0, cled1, cled2, cledG, cledR);
    buttonListener(buttons, speaker);
  }
  return 0;
}

6.スレッド間の通信(LEDの色を変える)

6.1 スレッド間通信

スレッドの間での通信は、必ず、チャネルを用いて行われる。共有メモリや共有データを用いて通信を行うと、資源へのアクセス競合が生じ、重大な問題を引き起こす可能性がある。チャネルを用いることでこれを避けることができる。

スレッドによる並列処理のプログラムを少し修正して、ボタンを押した時、スピーカーから音が出るだけでなく、LEDの色も変わる様にしたい。このとき、ボタンを押したという情報が、LEDを点滅回転させているスレッドに伝える必要がある。

そこで、ボタンを押した時、チャネルを通して信号が伝わるようにする。下のプログラムでは整数の0が送られるが値はあまり意味がない。ボタンを押した時、LEDを点滅させているスレッドに、チャネルを通して信号が送られてくるので、これを受けてLEDの色を変える。

mainのプログラムは、チャネルcを介して通信を行うので、次のようにプログラムを変更する。

  par {
    chan c;
    cycleLED(cled0, cled1, cled2, cledG, cledR, c);
    buttonListener(buttons, speaker, c);
  }

となる。

buttonListenerはボタンが押された時チャネルに信号を送り出す。これは、

    c <: 0;

で行われる。

cycleLED側では、ボタンが押された時、チャネルを介してイベントが発生するので

      case c :> int :
        isGreen = !isGreen;
        break;

をプログラムの中に組み入れてあげればよい。

また、cycleLEDとbuttonListenerの関数では、チャネルの型はchanendとする。

6.2 プログラム

#include <platform.h>
#define TDELAY 100000
#define TLENGTH 500
void buttonListener(in port b, out port spkr, chanend c) {
  timer tmr;
  int t, isOn = 1;
  while (1) {
    b when pinsneq(0xf) :> void;
    c <: 0;
    tmr :> t;
    for (int i=0; i<TLENGTH; i++) {
      isOn = !isOn;
      t += TDELAY;
      tmr when timerafter(t) :> void;
      spkr <: isOn;
    }
  }
}
#define FLASH_PERIOD 10000000
#define CYCLE_PERIOD 60000000
int cycleLED(out port cled0, out port cled1, out port cled2, out port cledG, out port cledR, chanend c) {
  unsigned ledOn = 1;
  unsigned ledVal = 1;
  timer tmrF, tmrC;
  unsigned timeF, timeC;
  int isGreen = 1;
  tmrF :> timeF;
  tmrC :> timeC;
  while (1) {
    select {
      case tmrF when timerafter(timeF) :> void :
        ledOn = !ledOn;
        if (isGreen) {
          cledG <: ledOn;
          cledR <: 0;
        }
        if(!isGreen) {
          cledR <: ledOn;
          cledG <: 0;
        }
        timeF += FLASH_PERIOD;
        break;
      case tmrC when timerafter(timeC) :&gt; void :
        cled0 <: ledVal;
        cled1 <: (ledVal &gt;&gt; 4);
        cled2 <: (ledVal &gt;&gt; 8);
        ledVal <<= 1;
        if (ledVal == 0x1000)
          ledVal = 1;
        timeC += CYCLE_PERIOD;
        break;
      case c :> int :
        isGreen = !isGreen;
        break;
    }
  }
  return 0;
}
in port buttons = PORT_BUTTON;
out port speaker = PORT_SPEAKER;
out port cled0 = PORT_CLOCKLED_0;
out port cled1 = PORT_CLOCKLED_1;
out port cled2 = PORT_CLOCKLED_2;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;
int main(void) {
  chan c;

  par {
    cycleLED(cled0, cled1, cled2, cledG, cledR, c);
    buttonListener(buttons, speaker, c);
  }
  return 0;
}

6.3 問題

プログラムでは、次のようになっている。

        if (isGreen) {
          cledG <: ledOn;
          cledR <: 0;
        }
        if(!isGreen) {
          cledR <: ledOn;
          cledG <: 0;
        }

これを、次のように変えたとする。

        if (isGreen) {
          cledG <: ledOn;
        }
        if(!isGreen) {
          cledR <: ledOn;
        }

LEDの振舞いがどのように変わるか、実際に動作させて、調べなさい。