ソフトウェア開発って、他人が過去に作ったものに対する発見を重ねるみたいなところがあって、人工的に作られた過去の遺物を発見してうまく解釈するという、なんかよくSFモノにある、失なわれた技術を発掘していくみたいな、そういう感じ。

ソフトウェアは書いた通りにしか動かない。ソフトウェアのどこかの部品が壊れて(バグって)いたら必ず、なにかしら観測可能な状態に落としこめるような環境が整っている。

ハードウェア部分は、人間が工夫して設計しているとはいえ、基本は物理現象(電子の動き)を利用していて、それが自分には不思議で面白い。ソフトウェアレイヤーをいじっている限りでは物理現象を利用しているという感覚は一切なく「他人がどういう意図で設計したか?」という、物理というよりは精神なことを考えていることが多い。

ハードウェア、回路レベルの設計は全体的にパズル的で、動いているのが不思議な感じがする。ソフトウェアでこういうパズル的なことをやろうとしたらテストを書かないと殺されると思うけど、頑張ってだいたいうまく動いてる感じで感心すると同時に、こんなので大丈夫なのかと心配になる。

しかし実際には精度良くハードウェアは動くように設計されていて、そういう頑張ってる感じのパズル的に動くロジックの大量の組合せでCPUが構成されて、その上で機械語が実行されていて、機械語を生成するための言語があって、Cから機械語を生成するためのツールチェインがあって、それらを利用してさらに抽象化されたレイヤーで書けるLLがあって、なんだか考えていると、LLでコードを書くのはジェンガの上のほうでコードを書いている感じに思えてくる。

GPIO の操作はいろいろやる方法があるみたいだけど、LL からだと sysfs への IO を行うのが一番簡単っぽい。以下のような感じでかなり簡単に書ける。この程度だとライブラリを使う必要はない。

pin の数値は、RPi Low-level peripherals で書いてあるような GPIO n の n の部分を指定する。(物理的なピンの並びとは関係ない)

  • export で指定した GPIO ピンを使えるようにする
  • direction で方向を指定する (入力か、出力か)
  • read/write で 0/1 を読み書きする

この例だと GPIO 25 (22pin) で指定時間ごとに論理が反転する。

#!/usr/bin/env ruby
# coding: utf-8

module GPIO
	def self.export(pin)
		File.open("/sys/class/gpio/export", "w") do |f|
			f.write(pin)
		end
	end

	def self.unexport(pin)
		File.open("/sys/class/gpio/unexport", "w") do |f|
			f.write(pin)
		end
	end

	def self.direction(pin, direction)
		[:in, :out].include?(direction) or raise "direction must be :in or :out"
		File.open("/sys/class/gpio/gpio#{pin}/direction", "w") do |f|
			f.write(direction)
		end
	end

	def self.read(pin)
		File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
			f.read.to_i
		end
	end

	def self.write(pin, val)
		File.open("/sys/class/gpio/gpio#{pin}/value", "w") do |f|
			f.write(val && val.nonzero? ? "1" : "0")
		end
	end
end

GPIO.export(25)
GPIO.direction(25, :out)

led = true
loop do
	GPIO.write(25, led)
	led = !led
	sleep 0.5
end
  1. トップ
  2. tech
  3. Ruby で sysfs 経由での GPIO 操作
  1. トップ
  2. raspberrypi
  3. Ruby で sysfs 経由での GPIO 操作

無線のPCモールスUSBインターフェイスって結構高価なのが多いんだけど、Raspberry Pi だと GPIO 経由で普通にキーイングできるし、単体で HDMI やコンポジットビデオ出力できるから、これで十分すぎる感じがする。

GPIO に UART があるけど、デフォルトではシリアルコンソールとして使うことが想定されていて、カーネルメッセージとかが流れる。これを無効にして、普通のシリアルポートとして使う。

/etc/inittab を書きかえる。ttyAMA0 の行をコメントアウトする

#Spawn a getty on Raspberry Pi serial line
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

/boot/cmdline.txt を書きかえる。デフォルトだと以下のようになっているので

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
  • console=ttyAMA0,115200 を削除 (シリアルコンソールのオプション)
  • kgdboc=ttyAMA0,115200 を削除 (カーネルデバッグ用のオプション ref. kgdboc

して以下のように

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

で reboot すると普通にユーザレベルで自由に使えるようになる。

3.3V なので、RS-232C レベルにするなら 3.3V でも使えるドライバICを使う必要がある。ICL3232 だと100円ぐらい。

回路

FET らへんは GPIO 25 (22pin) をオンにしているときだけスイッチを入れるための仕組みなので RS-232C のためには必須ではない (GND を普通に繋ぐだけ)

  1. トップ
  2. tech
  3. Raspberry Pi の GPIO でシリアル通信
  1. トップ
  2. raspberrypi
  3. Raspberry Pi の GPIO でシリアル通信

なんとなく i2c-dev.h とかで定義されているAPIを呼ばないと使えないのかなあと思っていたけど、デバイスファイルの読み書きと ioctl だけで普通に使うことができた。

書きこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • write する

I2C 的には START -> SLAVE + W -> DATA送信 -> STOP が行われる。

読みこみ

  • /dev/i2c-1 とかを rw+ で open
  • ioctl で I2C_SLAVE コマンドを使ってスレーブアドレスを指定する
  • 読みこみたいアドレスを write する
  • read す

I2C 的には START -> SLAVE + W -> DATA送信 -> ReSTART -> SLAVE + R -> DATA受信 -> STOP が行われる。

たぶん一度 write/read するたびに Repeated Start が送られるのかな。

Ruby での例

Ruby でやる場合以下のような感じでいけた。デバイスによってはうまくいかないかもしれない。

class I2CDevice
	# ioctl command
	# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
	I2C_SLAVE       = 0x0703

	attr_accessor :address

	def initialize(address)
		@address = address
	end

	def i2cget(address)
		i2c = File.open("/dev/i2c-1", "r+")
		i2c.ioctl(I2C_SLAVE, @address)
		i2c.write(address.chr)
		ret = i2c.read(1).ord
		i2c.close
		ret
	end

	def i2cset(*data)
		i2c = File.open("/dev/i2c-1", "r+")
		i2c.ioctl(I2C_SLAVE, @address)
		i2c.write(data.pack("C*"))
		i2c.close
	end
end

  1. トップ
  2. tech
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. i2c
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. raspberrypi
  3. Linux 上の LL で I2C する (Ruby)
  1. トップ
  2. ruby
  3. Linux 上の LL で I2C する (Ruby)