最新のWiringPi C言語ライブラリを用いてBCM2835を認識させる
Raspberry Pi Zero Wでwiringpi-perl (https://github.com/WiringPi/WiringPi-Perl)をインストールすると、実行時に次のようなエラーが出る
Unable to determine hardware version. I see: Hardware : BCM2835
これは wiringpi-perl の サブモジュール、WiringPi 内の wiringPi.c
がBCM2835に対応していないことが原因だ。
最新のgit ライブラリ WiringPi (https://github.com/WiringPi/WiringPi)では最新のプロセッサにも対応しているが、wiringpi-perlがサブモジュールを登録した時点のものがpullされてきているためだ。
まずは、wiringpi-perl の build.sh を見てみる
#!/bin/bash
echo "Updating submodule..."
git submodule update --init
echo "Generating bindings..."
swig2.0 -perl wiringpi.i
CORE=`perl -MConfig -e 'print $Config{archlib}'`/CORE
WIRINGPI=WiringPi/wiringPi
echo "Building against: $CORE"
gcc -fpic -c -Dbool=char -I$CORE wiringpi_wrap.c \
$WIRINGPI/wiringSerial.c \
$WIRINGPI/wiringShift.c \
$WIRINGPI/wiringPi.c \
$WIRINGPI/softPwm.c \
$WIRINGPI/softTone.c \
-D_GNU_SOURCE
gcc -shared wiringPi.o \
softPwm.o \
softTone.o \
wiringSerial.o \
wiringShift.o \
wiringpi_wrap.o \
-o wiringpi.so
この git submodule update --init
でサブモジュールがGitからダウンロードされてくる。
このダウンロードされてきたモジュールのファイルを、最新にするためには
git pull origin master
というコマンドを、サブモジュールのディレクトリ内で実行すればよい。
まず、問題のwiringPi.c
の過去バージョンと、最新バージョンの違いを見てみよう。
赤で着色した部分が、新しいCPUのBCM2835に対応できるかどうかの違い。
int piBoardRev (void) { FILE *cpuFd ; char line [120] ; char *c ; static int boardRev = -1 ; if (boardRev != -1) // No point checking twice return boardRev ; if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL) piBoardRevOops ("Unable to open /proc/cpuinfo") ; // Start by looking for the Architecture to make sure we're really running // on a Pi. I'm getting fed-up with people whinging at me because // they can't get it to work on weirdFruitPi boards... while (fgets (line, 120, cpuFd) != NULL) if (strncmp (line, "Hardware", 8) == 0) break ; if (strncmp (line, "Hardware", 8) != 0) piBoardRevOops ("No hardware line") ; if (wiringPiDebug) printf ("piboardRev: Hardware: %s\n", line) ; // See if it's BCM2708 or BCM2709 if (strstr (line, "BCM2709") != NULL) // Pi v2 - no point doing anything more at this point { piModel2 = TRUE ; fclose (cpuFd) ; return boardRev = 2 ; } else if (strstr (line, "BCM2708") == NULL) { fprintf (stderr, "Unable to determine hardware version. I see: %s,\n", line) ; fprintf (stderr, " - expecting BCM2708 or BCM2709.\n") ; fprintf (stderr, "If this is a genuine Raspberry Pi then please report this\n") ; fprintf (stderr, "to projects@drogon.net. If this is not a Raspberry Pi then you\n") ; fprintf (stderr, "are on your own as wiringPi is designed to support the\n") ; fprintf (stderr, "Raspberry Pi ONLY.\n") ; exit (EXIT_FAILURE) ; }
int piGpioLayout (void) { FILE *cpuFd ; char line [120] ; char *c ; static int gpioLayout = -1 ; if (gpioLayout != -1) // No point checking twice return gpioLayout ; if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL) piGpioLayoutOops ("Unable to open /proc/cpuinfo") ; // Start by looking for the Architecture to make sure we're really running // on a Pi. I'm getting fed-up with people whinging at me because // they can't get it to work on weirdFruitPi boards... while (fgets (line, 120, cpuFd) != NULL) if (strncmp (line, "Hardware", 8) == 0) break ; if (strncmp (line, "Hardware", 8) != 0) piGpioLayoutOops ("No \"Hardware\" line") ; if (wiringPiDebug) printf ("piGpioLayout: Hardware: %s\n", line) ; // See if it's BCM2708 or BCM2709 or the new BCM2835. // OK. As of Kernel 4.8, we have BCM2835 only, regardless of model. // However I still want to check because it will trap the cheapskates and rip- // off merchants who want to use wiringPi on non-Raspberry Pi platforms - which // I do not support so don't email me your bleating whinges about anything // other than a genuine Raspberry Pi. if (! (strstr (line, "BCM2708") || strstr (line, "BCM2709") || strstr (line, "BCM2835"))) { fprintf (stderr, "Unable to determine hardware version. I see: %s,\n", line) ; fprintf (stderr, " - expecting BCM2708, BCM2709 or BCM2835.\n") ; fprintf (stderr, "If this is a genuine Raspberry Pi then please report this\n") ; fprintf (stderr, "to projects@drogon.net. If this is not a Raspberry Pi then you\n") ; fprintf (stderr, "are on your own as wiringPi is designed to support the\n") ; fprintf (stderr, "Raspberry Pi ONLY.\n") ; exit (EXIT_FAILURE) ; }
最終的に、wiringpi-perl の build.sh を次のように修正する
#!/bin/bash
echo "Updating submodule..."
git submodule update --init
cd WiringPi
git pull origin master
cd ..
echo "Generating bindings..."
swig2.0 -perl wiringpi.i
〜 以下省略 〜
利用する関数を記したwiringpi.iをアップデート
wiringpi.i
に列挙されている関数群は、以前の(ver 2.32版の)ものだ。これを今回アップデートして利用する最新版の関数群に修正する。
%module wiringpi %apply unsigned char { uint8_t }; extern int wiringPiSetup (void) ; extern int wiringPiSetupSys (void) ; extern int wiringPiSetupGpio (void) ; extern void wiringPiGpioMode (int mode) ; extern void pullUpDnControl (int pin, int pud) ; extern void pinMode (int pin, int mode) ; extern void digitalWrite (int pin, int value) ; extern void pwmWrite (int pin, int value) ; extern int digitalRead (int pin) ; extern void shiftOut (uint8_t dPin, uint8_t cPin, uint8_t order, uint8_t val); extern uint8_t shiftIn (uint8_t dPin, uint8_t cPin, uint8_t order); extern void delay (unsigned int howLong) ; extern void delayMicroseconds (unsigned int howLong) ; extern unsigned int millis (void) ; extern int serialOpen (char *device, int baud) ; extern void serialClose (int fd) ; extern void serialPutchar (int fd, uint8_t c) ; extern void serialPuts (int fd, char *s) ; extern int serialDataAvail (int fd) ; extern int serialGetchar (int fd) ; extern void serialPrintf (int fd, char *message, ...) ; extern void pwmSetMode (int mode) ; extern void pwmSetRange (unsigned int range) ; extern void pwmSetClock (int divisor) ; %{ #include "WiringPi/wiringPi/wiringPi.h" #include "WiringPi/wiringPi/wiringShift.h" #include "WiringPi/wiringPi/wiringSerial.h" %}
%module wiringpi %apply unsigned char { uint8_t }; /***** * wiringPi.h *****/ extern void wiringPiVersion (int *major, int *minor) ; extern int wiringPiSetup (void) ; extern int wiringPiSetupSys (void) ; extern int wiringPiSetupGpio (void) ; extern int wiringPiSetupPhys (void) ; extern void pinModeAlt (int pin, int mode) ; extern void pinMode (int pin, int mode) ; extern void pullUpDnControl (int pin, int pud) ; extern int digitalRead (int pin) ; extern void digitalWrite (int pin, int value) ; extern unsigned int digitalRead8 (int pin) ; extern void digitalWrite8 (int pin, int value) ; extern void pwmWrite (int pin, int value) ; extern int analogRead (int pin) ; extern void analogWrite (int pin, int value) ; extern int piGpioLayout (void) ; extern void piBoardId (int *model, int *rev, int *mem, int *maker, int *overVolted) ; extern int wpiPinToGpio (int wpiPin) ; extern int physPinToGpio (int physPin) ; extern void setPadDrive (int group, int value) ; extern int getAlt (int pin) ; extern void pwmToneWrite (int pin, int freq) ; extern void pwmSetMode (int mode) ; extern void pwmSetRange (unsigned int range) ; extern void pwmSetClock (int divisor) ; extern void gpioClockSet (int pin, int freq) ; extern unsigned int digitalReadByte (void) ; extern unsigned int digitalReadByte2 (void) ; extern void digitalWriteByte (int value) ; extern void digitalWriteByte2 (int value) ; extern void delay (unsigned int howLong) ; extern void delayMicroseconds (unsigned int howLong) ; extern unsigned int millis (void) ; extern unsigned int micros (void) ; /***** * wiringSerial.h *****/ extern int serialOpen (const char *device, const int baud) ; extern void serialClose (const int fd) ; extern void serialFlush (const int fd) ; extern void serialPutchar (const int fd, const unsigned char c) ; extern void serialPuts (const int fd, const char *s) ; extern void serialPrintf (const int fd, const char *message, ...) ; extern int serialDataAvail (const int fd) ; extern int serialGetchar (const int fd) ; %{ #include "WiringPi/wiringPi/wiringPi.h" #include "WiringPi/wiringPi/wiringShift.h" #include "WiringPi/wiringPi/wiringSerial.h" %}
このファイルをperlのモジュールファイル(.pm)に変換するswig
のコマンドは
swig2.0 -perl wiringpi.i
build.sh を実行する前に、wiringpi.pm
ファイルは念の為 削除しておくほうが良い。
コンパイルを行い、モジュールファイルをインストール
コンパイルを行う
./build.sh
wiringpi-perl のコンパイルが終われば、次の2つのファイルをPerlモジュールのディレクトリにコピーする。
・wiringpi.pm
・wiringpi.so
コピー先は perl -e 'print @INC'
で表示されるディレクトリだが、その中のどれなのかというのは、適当なcpanモジュールをインストールするときの画面出力を見ていれば分かる。
今回は、 /usr/local/share/perl/5.22.1
に上述の2個のファイルをコピー (インストール)した。
※ コンパイラはcソースコードの全ての関数をsoに格納しているはずだが、ある特定の関数が含まれているかどうかは、シンボル一覧をダンプしてチェックすれば良い
readelf -s wiringpi.so
RPi::Const のインストール
WiringPiの各関数で用いる定数を定義しているcpanモジュールをインストールしておくと便利
sudo cpan RPi::Const
サンプル・プログラム
pwmでLEDの明るさを変化させるサンプル
#!/usr/bin/perl use strict; use warnings; use wiringpi; use RPi::Const; use Time::HiRes 'usleep'; # IMPORTANT ! : Rpi BCM_GPIO = 18 is equivalent to WiringPi GPIO = 1 use constant GPIO_PIN => 1; if( wiringpi::wiringPiSetup()==-1 ){ die 1; } wiringpi::pinMode(GPIO_PIN, RPi::Const::PWM_OUT); wiringpi::pwmSetMode(RPi::Const::PWM_MODE_BAL); wiringpi::pwmSetRange(100); wiringpi::pwmSetClock(218); for(my $j=0; $j<5; $j++){ for(my $i=0; $i<100; $i++){ wiringpi::pwmWrite(GPIO_PIN,$i); usleep(30*1000); # 30ミリ秒 } wiringpi::pwmWrite(GPIO_PIN,0); usleep(500*1000); # 500ミリ秒 } wiringpi::pinMode(GPIO_PIN, RPi::Const::OUTPUT); wiringpi::digitalWrite(GPIO_PIN, RPi::Const::LOW);