ししちにじゅうはち 4x7=28

よんたったー https://twitter.com/keita44_f4

SML with マイコンボード

この記事はML Advent Calendar 2014 14日目の記事です。

最近は、お仕事でマイコンボードプログラミングを書いています。
現在はBeagle Bone Black(以下BBB)をいじっています。
ところでこのマイコンボードはArmでフルのLinuxを動かすことができます。
さらにこんなコメントが。

!!!!

もちろんBBBは各種GPIOやLEDをLinuxファイルシステムとして提供してくれています。
つまりファイルの読み書きさえできて、Arm対応の言語なら何でもマイコン操作ができるはずです。
そこで今回はMltonによるマイコンボードプログラミングを紹介します。

環境

  • Beagle Bone Black (Rev C)
    • Ubuntu 14.10 utopicをインストール済み
    • apt-getでMltonのインストール済み

Beagle Bone BlackのGPIOの操作方法

  • 以下のサイトを参考にしました
    • shellからの操作がとても参考になる。
    • ピン番号についての一覧エクセル/PDFも便利。

http://www.si-linux.co.jp/techinfo/index.php?BeagleBoneBlack

# echo 60 > /sys/class/gpio/export             # 60番GPIOを使用
                                               # 新しいディレクトリgpio60ができる
# echo out > /sys/class/gpio/gpio60/direction  # outputに指定
# echo 0 > /sys/class/gpio/gpio60/value        # HIGH出力
# echo 1 > /sys/class/gpio/gpio60/value        # LOW出力

SMLによるGPIOモジュールの例(Arduino風)

上記シェルコマンドの動作をそのまま落とし込むだけです。
100行以下の小さなお試しプログラムです。

(* DIGITAL_GPIO.sig *)
signature DIGITAL_GPIO =
sig
  type pin = int
  datatype mode = OUTPUT | INPUT (* | INPUT_PULLUP *)
  datatype value = HIGH | LOW
  val pinMode : pin * mode -> unit
  val digitalWrite : pin * value -> unit
  (* 省略 val digitalRead : pin -> value *)
end
(* DigitalGPIO.sml *)
structure DigitalGPIO : DIGITAL_GPIO =
struct
  type pin = int

  datatype mode = OUTPUT | INPUT

  datatype value = HIGH | LOW

  val path = "/sys/class/gpio"

  fun startPin pin =
      let
        val path = path ^ "/export"
        val outs = TextIO.openOut path
        val () = TextIO.output (outs, Int.toString pin)
        val () = TextIO.closeOut outs
      in
        ()
      end

  fun setMode (pin, mode) =
      let
        val path = path ^ "/gpio" ^ Int.toString pin ^ "/direction"
        val modeString = case mode of
                             OUTPUT => "out"
                           | INPUT => "in"

        val outs = TextIO.openOut path
        val () = TextIO.output (outs, modeString)
        val () = TextIO.closeOut outs
      in
        ()
      end handle IO.Io error => ()

  fun pinMode (pin, mode) =
      let
        val () = startPin pin
        val () = setMode (pin, mode)
      in
        ()
      end

  fun digitalWrite (pin, value) =
      let
        val path = path ^ "/gpio" ^ Int.toString pin ^ "/value"
        val v = case value of
                    HIGH => "1"
                  | LOW => "0"

        val outs = TextIO.openOut path
        val () = TextIO.output (outs, v)
        val () = TextIO.closeOut outs
      in
        ()
      end
end
(* Main.sml *)
structure Main
          : sig
            val main : unit -> unit
          end
=
struct
  fun init () = DigitalGPIO.pinMode (60, DigitalGPIO.OUTPUT)

  fun loop () =
      let
        val () = DigitalGPIO.digitalWrite (60, DigitalGPIO.HIGH)
        val () = OS.Process.sleep (Time.fromReal 0.5)
        val () = DigitalGPIO.digitalWrite (60, DigitalGPIO.LOW)
        val () = OS.Process.sleep (Time.fromReal 0.5)
      in
        loop ()
      end

  fun main () = (init (); loop ())
end

val () = Main.main ()

できたプログラムをBBB上で実行すると、P9-12番ピン - GND間に繋いだLEDが1秒周期でチカチカします。
やったね!

setMode関数でIo例外を握りつぶしています。
実行初回だけdirectionファイルがビジー状態のエラーがでるためです。
もしかするとexport設定後のdirectionファイルへのアクセスが早過ぎる?
この辺はまだ調査中です。

まとめ