最新の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);