🫐

Elixir で M5Stack CoreMP135 の液晶ディスプレイに色を塗る

2024/09/25に公開

本稿について

本稿ではプログラミング言語 Elixir を用いて小型 Linux PC である M5Stack CoreMP135(以下、「CoreMP135」と呼ぶ)の液晶ディスプレイ(LCD)に色を塗る方法について解説します。

拙稿 Elixir を M5Stack CoreMP135 上で動かす の内容に沿って Elixir を CoreMP135 にインストールしてあることが本稿の前提条件です。

フレームバッファとは

フレームバッファとは、ビデオディスプレイの表示内容を記憶するメモリ領域のことです。

CoreMP135 の上面に備わっている 320px × 240px の LCD には /dev/fb1 という名前のフレームバッファが対応しています。これにビットマップデータを書き込めば、LCD の表示を更新できます。

CoreMP135 の LCD の各ピクセルは、16 ビットのデータで表現されます。最初の 5 ビットが赤、次の 6 ビットが緑、最後の 5 ビットが青を表します。ただし、緑を表す 6 ビットの先頭 1 ビットは使用されません。

また、CoreMp135 のプロセッサ(Cortex-A7)がビッグエンディアンで、CoreMP135 のフレームバッファはリトルエンディアンであるため、16 ビットデータの前半 8 ビットと後半 8 ビットを入れ替える必要があります。

エンディアン

複数バイトからなるデータ(ワード)をコンピュータの記憶装置に書き込む際のバイトの並び順に関する規則をエンディアン(endianness)と呼びます。ワードの最上位バイトを最小のアドレスに、最下位バイトを最大のアドレスに格納する規則をビッグエンディアン、ワードの最上位バイトを最大のアドレスに、最下位バイトを最小のアドレスに格納する規則をリトルエンディアンと呼びます。

Elixir スクリプト(1)

次に示すのは、私が作成した Elixir スクリプト paint_red.exs です。

fb_width = 320
fb_height = 240

<<a, b>> = <<31::5, 0::6, 0::5>>
red_pixel = <<b, a>>
pixels = List.duplicate(red_pixel, fb_width * fb_height)
data = :erlang.list_to_bitstring(pixels)

File.open("/dev/fb1", [:binary, :write], fn file ->
  IO.binwrite(file, data)
end)

このスクリプトを CoreMP135 内の適当なディレクトリにおいて elixir paint_red.exs コマンドで実行すると、LCD 全体が赤色で塗りつぶされます。

ソースコードの解説(1)

4 行目をご覧ください。

<<a, b>> = <<31::5, 0::6, 0::5>>

<<>>ビットストリングを作るための記号です。等号 = の右辺にある 31::50111110::6000000::500000 というビットストリングを作るという意味となり、全体としては 0111110000000000 というビットストリングが作られます。

等号 = の左辺にある <<a, b>> は、右辺のビットストリングの前半 8 ビットを変数 a に、後半 8 ビットを変数 b にセットするという意味になります。

5 行目をご覧ください。

red_pixel = <<b, a>>

変数 ab の値を用いてビットストリングを作っています。4 行目で作られた 16 ビットデータの前半 8 ビットと後半 8 ビットを入れ替えたものが変数 red_pixel にセットされます。これが赤色のピクセルデータです。

6 行目をご覧ください。

pixels = List.duplicate(red_pixel, fb_width * fb_height)

関数 List.duplicate/2 を用いて、76,800(320 × 240)個の同一のピクセルデータを要素とするリストを作っています。

7 行目をご覧ください。

data = :erlang.list_to_bitstring(pixels)

Erlang の関数 list_to_bitstring/1 により、ビットストリングのリストを連結して長大なビットストリングを作っています。これがビットマップデータです。

9-11 行をご覧ください。

File.open("/dev/fb1", [:binary, :write], fn file ->
  IO.binwrite(file, data)
end)

関数 File.open/3 を用いてフレームバッファ /dev/fb1 をオープンし、関数 IO.binwrite/2 を用いてビットマップデータをフレームバッファに書き込んでいます。

バイナリモードで書き込むため、関数 File.open/3 の第 3 引数にオプション [:binary, :write] を指定しています。この場合、データを書き込む関数として IO.write/2 ではなく IO.binwrite/2 を使う必要があります。

Elixir スクリプト(2)

次に示すのは、paint_red.exs を改造して作った Elixir スクリプト paint_eight_colors.exs です。

fb_width = 320
fb_height = 240

pixels =
  for x <- 0..(fb_height - 1), y <- 0..(fb_width - 1) do
    {r, g, b} =
      cond do
        x < 120 && y < 80 -> {0, 0, 0}
        x < 120 && y < 160 -> {31, 0, 0}
        x < 120 && y < 240 -> {0, 31, 0}
        x < 120 -> {31, 31, 0}
        y < 80 -> {0, 0, 31}
        y < 160 -> {31, 0, 31}
        y < 240 -> {0, 31, 31}
        true -> {31, 31, 31}
      end

    <<a, b>> = <<r::5, g::6, b::5>>
    <<b, a>>
  end

data = :erlang.list_to_bitstring(pixels)

File.open("/dev/fb1", [:binary, :write], fn file ->
  IO.binwrite(file, data)
end)

このスクリプトを CoreMP135 内の適当なディレクトリにおいて elixir paint_eight_colors.exs コマンドで実行すると、LCD の表示が次のように変化します。

LCD

ソースコードの解説(2)

5 行目をご覧ください。

  for x <- 0..(fb_height - 1), y <- 0..(fb_width - 1) do

変数 fb_heightfb_width の値を埋め込んでしまうと、こうなります。

  for x <- 0..319, y <- 0..239 do

Elixir のリスト内包表記により、変数 xy にビットマップのピクセルの座標をセットしながら、doend の間のコードを評価して、リストを作り上げます。doend の間のコードは 76,800(320 × 240)回評価されます。

6-16 行をご覧ください。

    {r, g, b} =
      cond do
        x < 120 && y < 80 -> {0, 0, 0}
        x < 120 && y < 160 -> {31, 0, 0}
        x < 120 && y < 240 -> {0, 31, 0}
        x < 120 -> {31, 31, 0}
        y < 80 -> {0, 0, 31}
        y < 160 -> {31, 0, 31}
        y < 240 -> {0, 31, 31}
        true -> {31, 31, 31}
      end

変数 xy の値から 3 個の整数の組を作って変数 r, g, b にセットしています。それぞれ赤、緑、青の色の値を示します。値は 0 から 31 までです。

3 個の整数の組は、それぞれ次のような色に対応しています:

  • {0, 0, 0}: 黒
  • {31, 0, 0}: 赤
  • {0, 31, 0}: 緑
  • {31, 31, 0}: 黄色
  • {0, 0, 31}: 青
  • {31, 0, 31}: マゼンタ
  • {0, 31, 31}: シアン
  • {31, 31, 31}: 白

18-19 行をご覧ください。

    <<a, b>> = <<r::5, g::6, b::5>>
    <<b, a>>

<<>> についてはすでに説明しています。r::5 は変数 r の値から 5 ビットのビットストリングを作るという意味です。上記の式により 16 ビットのビットストリングができあがります。

例えば、整数の組 {31, 31, 0} からは 1110000011111011 というビットストリングが作られます。これは黄色のピクセルデータです。

備考

  • 当初、本稿は「Elixir で CoreMP135 のフレームバッファを操作する①」というタイトルで公開されました。
  • Nerves LivebookでRoller485を動かしてみた の著者 @GeekMasahiro 様よりの指摘を受けて 2024 年 10 月 5 日に本稿は大幅に改訂されました。それまで CoreMP135 のフレームバッファがリトルエンディアンであることに気づいていなかったため、本文およびソースコードに重大な誤りがありました。

参考文献

Discussion