Raspberry Pi 日記 (part2)

長嶋 洋一


2013年6月29日(土)

もう週末である。 明日の日曜日の午前には、SUACのプロキシサーバ交換工事が予告されていて仕事に出て来れない(;_;)が、その分、この土曜日の予定は満載である。 バスで大学に出て来て、午後には「40虎」のミーティングがあり、今日は予約しておいた講堂とギャラリーの探検である。 その後、某秘蔵映像の上映会(勉強会)があり、晩にはお楽しみの予定もある(のでクルマでなくバスで来た(^_^;))。 そのバス車内でRaspberry Piで次に実験してみたい事も浮かんできていて、午前中ガシガシ進めよう、と思っていたところにSOS連絡があり、急遽タクシーを大学に呼んで、自宅に行ってスグまた大学に戻ってきたりして時間を取られたが、 可愛いこの子 のためだ、仕方ない。(^_^;)

さてRaspberry Piでの実験であるが、C言語で゛GPIOポートをアクセスするプログラムが出来る、と判ったので、その部分ではPythonのGPIOアクセスはパスする事にしたが、せっかく高機能なRaspberry Piなので、あれこれ他の仕事も同時に(時分割多重化)行うためには、やはり標準推奨環境であるPythonは必要だ、という当たり前の事に気付いたのがスタートラインである。 すっかり油断して、もうぼちぼちPythonは忘却の彼方に行きかけていたが、ふーみん本の残された勉強項目などは、ほとんどPythonベースのアプローチなのだった。 そこでまず、残っていた「blink.c」をベースに、以下のような「test.c」を作って、まずは「コマンドラインとのインターフェース」という、C言語の標準入出力「stdio.h」の復習から始めることにした。「stdlib.h」と「fcntl.h」は、ファイルアクセス等で必要だ、とエラーが出たら追加することにした。

#include <stdio.h>
#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main(int argc, char *argv[])
{
	int i, j, k;

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++)
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	EXT_out(0, 1);
	GPIO_out(0, 1);

	printf("argc = %d\n", argc);
	printf("argv[0] = %s\n", argv[0]);
	for(j=1; j<argc; j++){
		printf("argv[%d] = %s\n", j, argv[j]);
	}

/*
	while(1){
		for (j=0; j<128; j++) {
			EXT_out(j, 1);
			for (k=0; k<1000; k++) {
				for (i=0; i<256; i++) {
					GPIO_out(i, 1);
				}
			}
		}
	}
*/
	EXT_out(0, 1);
	GPIO_out(0, 1);
	bcm2835_close();
}
この結果は以下のようになり、無事にargcとargvの意味と、コマンドラインからオプション付きで呼び出すやり方を思い出した。 こんなC言語の基本中の基本は、初めて触れたのはもう25年以上の昔で、たぶん10年以上、まったく書いていなかった(^_^;)。

ここでしばし実験して、以下のような GPIO_out.c を完成させた。 これはオプションとして3つの数値が必要で、第1パラメータはGPIOポートの8ビット(port=0)かEXTポートの7ビット(port=1)かを指定、第2パラメータはそのデータ(GPIOなら0-255、EXTなら0-127)、第3パラメータは正論理にら0で負論理なら1である。

#include <stdio.h>
#include <bcm2835.h>

int assign[15] = {17,18,27,22,23,24,25,4,2,3,10,9,11,8,7};

void GPIO_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 255;
	for (i=0; i<8; i++) {
		bcm2835_gpio_write( assign[i], ( ( (data >>i) & 1) ^ mode ) );
	}
}

void EXT_out(int data, int mode){
	int i;
	mode = mode & 1;	// mode=0 : active high / mode=1 : active low
	data = data & 127;
	for (i=0; i<7; i++) {
		bcm2835_gpio_write( assign[i+8], ( ( (data >>i) & 1) ^ mode ) );
	}
}

int main(int argc, char *argv[])
{
	int i, p[3];

	if(argc != 4){
		printf("Format Error (;_;)\n");
		printf("usage : GPIO_out para1 para2 para3\n");
		printf("\tpara1 = port : 0=GPIO[0-7], 1=EXT[0-6]\n");
		printf("\tpara2 = data : 0-255(GPIO), 0-127(EXT)\n");
		printf("\tpara3 = mode : 0=active_HI, 1=active_LOW\n");
		return 1;
	}
	for(i=0; i<3; i++){
		p[i] = atoi(argv[i+1]);
	}
//	printf("port=%d, data=%d, mode=%d\n", p[0], p[1], p[2]);

	if (!bcm2835_init()){
		printf("GPIO is not found.\n");
		return 1;
	}
	for (i=0; i<15; i++){
		bcm2835_gpio_fsel(assign[i], BCM2835_GPIO_FSEL_OUTP);
	}

	switch(p[0]){
		case 0:
			GPIO_out(p[1], p[2]);
			break;
		case 1:
			EXT_out(p[1], p[2]);
			break;
	}
	bcm2835_close();
}
作った後で数値の範囲外はチェックしていない・・・と思ったら、switch分でportの指定がチェックされていて、さらにサブルーチンではデータとモードのチェックをしていたので、そこそこしっかりしたものなった(^_^;)。 これで、コマンドラインからパラメータを添えて全15ビットの出力ポートを、個々のビットごとの操作でなく、8ビットまたは7ビットの数値として出力指定できる事になった。 これは汎用ツールとして使えるという想定なので、これも「sudo chown root GPIO_out」でオーナーをrootにして、「sudo chmod 4755 GPIO_out」で実行権限を変更した。

このようなGPIO汎用出力ツールが出来たところで、いよいよまずはC言語からのシステムコールとして、この「GPIO_out」に対して、今週やってきた「8ビットのGPIOポートを2進数で0から255までカウントアップして、ゼロに戻るとEXTポートが1だけカウントアップし、これが7ビットのEXTポートで0から127まで回ると一周」というのを試してみることにした。 全て1本のCプログラムの内部で回すのと、いちいちシステムコールを経由するのとで、スピードを比較するというのが第一の目的であり、これをC言語でなくPythonプログラムから同様に実現して実験・比較する、というのが第二の目的(これは忘れかけてきたPython思い出しのリハビリを兼ねる)である。

#include <stdio.h>

int main()
{
	int i, j;
	char s[64];

	for(j=0; j<128; j++){
		sprintf(s,"./GPIO_out %d %d %d", 1, j, 1);
		system(s);
		for(i=0; i<256; i++){
			sprintf(s,"./GPIO_out %d %d %d", 0, i, 1);
			system(s);
		}
	}
}

YouTube

そしてしばしあれこれ試して、上のようなシンプルなプログラムが完成した。 これはgccでなく普通に「cc test.c」としてコンパイルして「./a.out」で実行できるが、やはり、いちいちシステムコールをするという事で、処理スビードは目に見えて遅くなった。 上の動画のように、EXTポートの7ビットのうち「ビット0」をストップウォッチで測ったら、およそ2.9秒程度の点灯/消灯時間だった。 つまり、7ビットのLSBの点灯時間が2900ミリ秒とすると、この時間内に8ビットのGPIOポートを256回、アクセスしているので、計算上は内側のGPIO_out()をシステムコール経由で1回アクセスする処理時間は約11ミリ秒、という概算となる。


「Raspberry Pi日記」トップに戻る