2012/11/10からのアクセス回数 10645
mbedはC++のクラスライブラリとユーザの作成したデバイス用のソースコードが豊富で、 デバイスを接続して新しいガジェットを作るのに適しています。
このような環境を別のハードにも欲しいと思う人はいるもので、そらみみの声に IDEを使わずにLPCXpresso LPC1768のプログラムを開発する というブログにLPC1768用にmbed互換ライブラリの作成記事がありました。
mbed互換ライブラリでは、mbedのDigitalOut, BusOut, TextLCDが実装されているだけですが、 これをRaspberryPiに移植すれば、mbedユーザが作った豊富なライブラリを活用できるのではないかと思い移植してみることにしました。
WiringPiライブラリのインストールについては、 raspberrypi/Raspberry PiでIO制御 を参照してください。
最初にRaspberryPiのピン番号をmbedに習い1番ピンから反時計回りに割り振ることにしました。
これを
PinNames.hに定義しました。
mbed互換ライブラリにはwait_usしかありませんが、mbedのwait_api.hの関数を
wait_api.cに実装しました。残念ながら、wait_usはnanosleepシステム関数を使った場合、TextLCDが正常に動作しなかったためビジーウェイトで実装しました。
wait_msの実装は以下のような感じです。
void wait_ms(int ms)
{
struct timespec sleeper, dummy ;
sleeper.tv_sec = (time_t)(ms / 1000) ;
sleeper.tv_nsec = (long)(ms % 1000) * 1000000 ;
nanosleep (&sleeper, &dummy) ;
}
DigitalOutには、WiringPiのライブラリを使用したので、とてもすっきり実装できました。
DigitalOut.hで保持しているのは、ピン名(_pin)と対応するWiringPiのピン番号(_wiring)のみです。
protected: PinName _pin; int _wiring;
DigitalOut.cpp の実装は、ピン名のチェック以外はそのままWiringPiの関数digitalWrite, digitalReadを呼び出すだけです。
void DigitalOut::write(int value)
{
if (_wiring > 0) {
if (value) {
digitalWrite (_wiring, 1);
}
else {
digitalWrite (_wiring, 0);
}
}
}
int DigitalOut::read()
{
if (_wiring > 0) {
return (digitalRead(_wiring));
}
else {
return (0);
}
}
上記以外のBusOut.h, BusOut.cpp, TextLCD.h, TextLCD.cppはそのまま使用しました。
残るは
Makefileですが、以下の部分を変更しました。
WiringPiのライブラリが/usr/local/includeにインストールされているので、-I, -Lオプションで指定しています。また、wiringPiライブラリを使用しているので、LIBSに追加しています。
#DEBUG = -g -O0 INCLUDE = -I/usr/local/include -I. CFLAGS = $(DEBUG) -Wall $(INCLUDE) -Winline -pipe LDFLAGS = -L/usr/local/lib LIBS = -lwiringPi
GPIO18を使ってLEDを1秒間隔で点滅させるプログラムをLEDTest.cppを以下のように作成しました。
#include <mbed.h>
#include <stdlib.h>
#include <unistd.h>
DigitalOut led(p21);
int main()
{
while(1) {
led = !led;
wait_us(1000000);
}
}
ほとんどmbedのテンプレートと同じ形式でRaspberryPiで動かすことができます。
後閑さんの「C言語によるPICプログラミング入門」で紹介されていた3行表示のLCD
を使って、TextLCD.cppをテストしました。このLCDの接続は、
でデータ転送には、4bitのみを使っています。
私の備忘録として、接続コネクタのピンは以下のようになっています。
LCDTest.cpp でのlcdの定義は、
lcd(p4, p7, p8, p16, p18, p19)
とし、
に接続しました。
以下に動作中の様子を示します。
RaspberryPi版の追加機能として、I2Cを実装しました。
I2C.cpp では、デバイス/dev/i2c-0をオープンし、ioctlを使ってI2Cアドレスをセットした後に、
read, writeを行う簡単なものです。
I2C::I2C(PinName sda, PinName scl, const char *name)
{
_name = (char *)name;
_fd = -1;
// ハードI2Cのみをサポート(ピン固定)
if (sda == SDA && scl == SCL) {
_fd = open("/dev/i2c-0", O_RDWR);
}
}
int I2C::read(int address, char *data, int length, bool repeated) {
int len;
if (_fd >= 0) {
ioctl(_fd, I2C_SLAVE, address>>1);
if ((len = ::read(_fd, data, length)) > 0) {
// 今は、戻り値のサイズをチェックしない
return (0);
}
else
return (-1);
}
else
return (-1);
}
int I2C::write(int address, const char *data, int length, bool repeated) {
int len;
if (_fd >= 0) {
int addr = address>>1;
ioctl(_fd, I2C_SLAVE, addr);
if ((len = ::write(_fd, data, length)) > 0) {
// 今は、戻り値のサイズをチェックしない
return (0);
}
else
return (-1);
}
else
return (-1);
}
I2Cの例題として、LM73を使って温度を読み取るクラスを作成します。
#include <mbed.h>
#include "LM73.h"
LM73::LM73(PinName sda, PinName scl) : i2c(sda, scl)
{
char cmd[2];
// LM73設定
cmd[0] = 0x04; // register 4
cmd[1] = 0x60; // 14bit resolution
i2c.write( LM73_ADDR, cmd, 2);
// ポインタを0にしておく(readするだけで温度が読めるようになる)
cmd[0] = 0x00;
i2c.write( LM73_ADDR, cmd, 1);
}
LM73::~LM73()
{
}
float LM73::read()
{
char cmd[2];
i2c.read( LM73_ADDR, cmd, 2); // Send command string
int int_val = cmd[0] <<1 | cmd[1]>>7;
int ceil_val = ((cmd[1] & 0x7f)*200) >>8;
return float(int_val + ceil_val/100.0);
}
LM73との接続は以下のようにしました。
システムのIOを使用するため、互換ライブラリを使ったプログラムの実行には、sudoコマンドが必要です。
以下のようにI2CTestを実行します。
$ sudo ./I2CTest 22.18 22.21 22.21
今回作成したRaspberry版mbed互換ライブラリを以下におきます。
SPIを追加したバージョンは、以下を参照してください。
皆様のご意見、ご希望をお待ちしております。