PiFaceでキーボード入力すると各LEDが点滅するプログラムを作ってみた
PiFaceでキーボードから1~4の数字を入力すると各LED2~5が点滅するプログラムをC言語で作ってみました。
思いのほか複雑な(私にとっては)プログラムになり、試行錯誤が多かったため作成経過やプログラムの内容を詳細に書くことは難しいかもしれませんが備忘録として書いておきたいと思います。
ただ、4つのLEDを点滅させるだけのプログラムに何でそんなにと思われるかもしれませんが、点灯なら良かったのですが点滅させることを考えたために複雑になってしまったのです。
まず、LEDを複数個点滅させるためには点滅させるプログラムを複数個同時並行的(親プロセスと子プロセスに分けて)に動かしてやる必要があります。そして、4個のLEDとなると各プログラムの始めと終わりをPiFaceのスイッチだけではなくキーボードからも行った方が良いように思ったのでそれを行うためにも困難を生じたのでした。
複数のプログラム要するに親プロセスと子プロセスに分けて実行するためにはfork()と言うシステムコールを使ってやらねばなりませんでしたが、この使い方がよく分からなかったのでした。右のWebページhttp://www.ne.jp/asahi/hishidama/home/tech/c/fork.html等を参考にしたりして、子プロセスを起動する方法を真似たりしたのでした。
次に、この子プロセスを始めたり終わらせる、つまり、点滅を始めたり終わらせるためにどのようにしたら良いかを考えなくてはなりませんでした。PiFaceにはLEDのみが点滅できるものの数が6つありましたので、4つぐらいは動かせるようにしたいところでした。また、スイッチも4つしか有りませんでしたので開始か終了のボタンをキーボードに任せる必要がありました。Linuxでの標準的なキーボードの入力はgetchar()と言うAPIしかなくこれではキー入力の後にリターンキーを押す必要があります。これを何とか回避するためにgetch()を使いたいところでした。これを使うには、どのこWebページを見たのか忘れてしまいましたが、libncurses5-dev_5.9-10_armhf.devをインストールしてやる必要があります。
$ sudo apt-get install libncurses5-dev_5.9-10_armhf.dev
でインストールしました。当然、プログラムでは前処理で#include<curses.h>、更に日本語化のため#include <locale.h>してやる必要が出てきます。これだけではなく、setlocale(LC_ALL, "")を日本語化のために、initscr()をgetch()を始めるための関数として、endwin()を終わらせるための関数として書き込む必要があります。さらに、コンパイルするときに-lncurseswでリンクを指定してやる必要があります。何とも複雑です。右のWebページhttp://www.kushiro-ct.ac.jp/yanagawa/ex/2-game/01.htmlにその他にもcursesの使い方が載っている様です。
次に共通メモリです。親プロセスでキーボードで'q'を押すと子プロセスも終了するようにしてやりたかったのですが、それには、最初はグローバル変数で親プロセスと子プロセスの間で共通の変数を作ることが可能ではないかと思い実行してみましたが上手く行かず、次にポインタを使ってやればメモリ指定なので行けるかと思ったのですがこれも上手く行かず、結局たどり着いたのが共通メモリでした。共通メモリの考え方は簡単なのですが、これもプログラムでは前処理として#include<sys/types.h>、#include<sys/ipc.h>、#include<sys/shm.h>が必要で、また、変数を準備したり、共通メモリ・セグメントを新規作成するためにshmget()でif((id=shmget(IPC_PRIVATE,512,IPC_CREAT | 0666))==-1){}と言った見ても中身がよく分からないままなものを扱わなくてはなりません。さらには、共通メモリ・セグメントをプロセスのアドレス空間に付加としてshmat()を if((pt=shmat(id,0,0))==(void*)-1){}と言うポインタ変数のptにアドレスを代入してやったりで、ようやく、 *pt=getch()でキーボード入力を共通メモリに入れ込むことができました。
fork()で起動した子プロセスにも共通メモリ・セグメントをプロセスのアドレス空間に付加としてshmat()をif((pt=shmat(id,0,SHM_RDONLY))==(void*)-1){}を行ってやらなくてはならないと言うことです。ここでは、リードオンリーとしています。また、(void*)と言う呪文が現れます。おそらくptでは一文字しか扱えないのに==-1とするとマイナスの整数値または"-1"と言う2文字になってしまうためこの処理が必要なのだと思います。ここで疑問なのは、ptを子プロセス起動後に再定義しているのですが、idについては得に再定義しておらずそのまま親プロセスのidを使えることです。
さて今度は子プロセス内でif(pfio_digital_read(0)==1 || *pt=='q')break;としてスイッチS1が押されるかキーボードでqが押されていれば無限ループを終了してLED2の点滅リセットを行うところです。結局、色々やっておいて*ps=='q'ですませてしまうのですから何ともです。
動きは何となく分かるものの分からないところも多く危険な感じもします。また、グローバル変数やポインタでなぜ代替が効かないのかも私には分からないところです。
後は、後処理です。親プロセス、子プロセスともに無限ループを脱したあとでif(shmdt(pt)==-1){}で共有メモリ・セグメントを分離します。そして、親プロセスではif(shmctl(id,IPC_RMID,0)==-1){}で共有メモリ・セグメントを破棄します。
そして先にも述べたendwin();でgetch()を終わらせます。(正確には違う表現をした方が良いですが)
概ね以上のようなプログラムの中身です。
結局、ファイル名は「illuminate.c」としてgccによるコンパイルは
$gcc -L/usr/local/lib/ -lpiface-1.0 -lncursesw -o illuminate illuminate.c
の様になります。
実行は、
$ ./illuminate
で行います。
以下がソースですが突き合わせて分かっていただけるでしょうか。
私の頭の中もあまり整理されていないので皆さんもきっと分かりにくいでしょうね。
/*PiFace上のLEDがキーボードとボタン操作毎に点滅するプログラム*/
#include<stdlib.h>
#include<unistd.h>
#include <libpiface-1.0/pfio.h>
/*ファイルは/usr/local/include/libpiface-1.0/pfio.hにあります*/
#include<curses.h>/*Linuxでgetch()を利用するため*/
#include <locale.h>/*addstr()で日本語の表示を出来るようにするため*/
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main(void)
{
int id;/*共通メモリのidのための変数*/
char sw='\0';
char *pt;/*共通メモリのためのポインタ変数*/
pt=&sw;
pid_t pid[3];/*私には何の変数宣言か不明*/
setlocale(LC_ALL, "");/*日本語の利用が出来るようにするため*/
initscr();/*getch()を始めるための関数*/
addstr("キーボードの1~4を押すとLED2~5が点滅\n");
addstr("スイッチのS1を長押しするとLED2~5の点滅がリセット\n");
addstr("キーボードのqを押すとプログラムが終了\n");
pfio_init();/*Pifaceを動作を始めるための関数と思われる*/
if((id=shmget(IPC_PRIVATE,512,IPC_CREAT | 0666))==-1){/*共通メモリ・セグメントを新規作成*/
perror("shmget");/*もし共通メモリ・セグメントの新規作成に失敗したら(idが-1)エラー表示*/
exit(-1);
}
if((pt=shmat(id,0,0))==(void*)-1){/*共通メモリ・セグメントをプロセスのアドレス空間に付加。 (void*)については入れないとコンパイルの際に警告が発生する。1文字との比較のためと思われる。*/
perror("shmat");/*もし付加に失敗したらエラー表示*/
exit(-1);
}
else{
while(1) {/*無限ループです*/
usleep(50000);/*時間を置く*/
*pt=getch();/*キーボードが押された文字をsw[0]である*ptに代入*/
if(*pt=='1'){/*キーボード1が押されていれば*/
pid[0]=fork();/*子プロセスを複製*/
if(pid[0]<0){
perror("fork0");/*もし子プロセスの複製に失敗したらエラー表示*/
exit(-1);
}
else if(pid[0]==0){/*子プロセスの起動*/
char *pt;
if((pt=shmat(id,0,SHM_RDONLY))==(void*)-1){/*共通メモリ・セグメントをプロセスのアドレス空間に付加*/
perror("shmat");
exit(-1);
}
while(1) {/*無限ループです*/
pfio_digital_write(2, 1);/*LEDの2番を点灯*/
usleep(100000);/*100000マイクロ秒間続ける*/
pfio_digital_write(2, 0);/*LEDの2番を消灯(滅)*/
usleep(300000);/*200000マイクロ秒間続ける*/
if(pfio_digital_read(0)==1 || *pt=='q')break;/*スイッチS1が押されるかキーボードでqが押されていれば無限ループを終了でLED2の点滅リセット*/
}
if(shmdt(pt)==-1){/*共有メモリ・セグメントを分離*/
perror("shmdt");
exit(-1);
}
_exit(0);
}
}
else if(*pt=='2'){/*キーボード2が押されていれば*/
pid[1]=fork();/*子プロセスを複製*/
if(pid[1]<0){
perror("fork1");
exit(-1);
}
if(pid[1]==0){
char *pt;
if((pt=shmat(id,0,SHM_RDONLY))==(void*)-1){
perror("shmat");
exit(-1);
}
while(1) {
usleep(100000);
pfio_digital_write(3, 1);/*LEDの3番を点灯*/
usleep(100000);
pfio_digital_write(3, 0);/*LEDの3を消灯(滅)*/
usleep(200000);
if(pfio_digital_read(0)==1 || *pt=='q')break;
}
if(shmdt(pt)==-1){
perror("shmdt");
exit(-1);
}
_exit(0);
}
}
if(*pt=='3'){/*キーボード3が押されていれば*/
pid[2]=fork();/*子プロセスを複製*/
if(pid[2]<0){
perror("fork2");
exit(-1);
}
if(pid[2]==0){
char *pt;
if((pt=shmat(id,0,SHM_RDONLY))==(void*)-1){
perror("shmat");
exit(-1);
}
while(1) {
usleep(200000);
pfio_digital_write(4, 1);/*LEDの4番を点灯*/
usleep(100000);
pfio_digital_write(4, 0);/*LEDの4番を消灯(滅)*/
usleep(100000);
if(pfio_digital_read(0)==1 || *pt=='q')break;
}
if(shmdt(pt)==-1){
perror("shmdt");
exit(-1);
}
_exit(0);
}
}
if(*pt=='4'){/*キーボード4が押されていれば*/
pid[3]=fork();/*子プロセスを複製*/
if(pid[3]<0){
perror("fork3");
exit(-1);
}
if(pid[3]==0){
char *pt;
if((pt=shmat(id,0,SHM_RDONLY))==(void*)-1){
perror("shmat");
exit(-1);
}
while(1) {
usleep(300000);
pfio_digital_write(5, 1);/*LEDの5番を点灯*/
usleep(100000);
pfio_digital_write(5, 0);/*LEDの5番を消灯(滅)*/
if(pfio_digital_read(0)==1 || *pt=='q')break;
}
if(shmdt(pt)==-1){
perror("shmdt");
exit(-1);
}
_exit(0);
}
}
if(*pt=='q')break;/*キーボードqが押されていれば無限ループを終了でプログラムを終了*/
}
}
usleep(500000);/*子プロセスが終了するまでの時間稼ぎ*/
if(shmdt(pt)==-1){/*共有メモリ・セグメントを分離*/
perror("shmdt");
exit(-1);
}
if(shmctl(id,IPC_RMID,0)==-1){/*共有メモリ・セグメントを破棄*/
perror("shmctl");
exit(-1);
}
pfio_deinit();/*Pifaceを動作を終わらせる関数と思われる*/
endwin();/*getch()を終わらせるための関数*/
return 0;
}
« PiFaceでクイズの早押しボタンのプログラムを作ってみた | トップページ | マウス、キーボード共用ソフトsynergyを導入してみました »
「パソコン・インターネット」カテゴリの記事
- logicool M720 Triathlon マルチデバイスマウスが故障(2022.08.20)
- UbuntuのFirefoxだと雑音が入る(未解決)(2022.02.13)
- イントロンのブログが60,000カウントを達成(2022.01.13)
- GrovePi+をRaspberry Pi 4に導入してみた(上手くゆかなかった)(2021.12.26)
- Raspberry Pi 4でVLCメディアプレイヤー起動時にフリーズがなぜか解消(2021.12.20)
« PiFaceでクイズの早押しボタンのプログラムを作ってみた | トップページ | マウス、キーボード共用ソフトsynergyを導入してみました »


コメント