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でプログラムにより参照される。
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; }
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) :> 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; }
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 <platform.h> #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<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) :> void : cled0 <: ledVal; cled1 <: (ledVal >> 4); cled2 <: (ledVal >> 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の振舞いがどのように変わるか、実際に動作させて、調べなさい。